我发现最好的方法是将应用程序的设计转换为某种形式的 Model-View-* 模式,对于 Winforms,Model-View-Presenter 1通常很合适。
这样,您就有了一个负责所有逻辑的演示者/控制器类,并且您拥有保存 UI 代码的视图。在 Winforms 中,您通常通过让表单类实现一个 IView 接口来实现这一点,该接口定义了它可以做的所有事情。
然后,您将表单(以声明为接口的形式传递)提供给演示者 - 构造函数依赖注入是传递它的典型方式。
因此,在您的示例中,您将AccessToCsvFileVerificationInputs
方法移动到演示者类中,并向演示者传递一个实现正确接口的表单实例,说明该表单可以执行的所有操作。
像这样的东西:
public class CsvFilePresenter
{
private ICsvFileView view_;
public CsvFilePresenter(ICsvFileView view)
{
view_ = view;
}
public void AccessToCsvFileVerificationInputs(bool access)
{
// Code omitted for brevity
}
}
现在,在该方法中,在您直接引用控件等 UI 功能的任何地方,您都引用了界面:
public void AccessToCsvFileVerificationInputs(bool access)
{
view_.EnableSelectCSVFilePath = access;
view_.EnableNumberOfColumns = access;
view_.EnableCurrencyPair = access;
// And so on...
}
我故意将控件集合排除在外 - 您如何处理这些将取决于具体情况。
例如,您有这个 ICsvFileView 接口:
public interface ICsvFileView
{
bool EnableSelectCSVFilePath { get; set; }
bool EnableNumberOfColumns { get; set; }
bool EnableCurrencyPair { get; set; }
}
此 ICsvFileView 的具体实现可能如下所示:
public partial class Form3 : Form, ICsvFileView
{
public Form3()
{
InitializeComponent();
}
bool ICsvFileView.EnableSelectCSVFilePath
{
get
{
return btnSelectCsvFilePath.Enabled;
}
set
{
btnSelectCsvFilePath.Enabled = value;
}
}
bool ICsvFileView.EnableNumberOfColumns
{
get
{
return nudNumberOfColumns.Enabled;
}
set
{
nudNumberOfColumns.Enabled = value;
}
}
bool ICsvFileView.EnableCurrencyPair
{
get
{
return cbCurrencyPair.Enabled;
}
set
{
cbCurrencyPair.Enabled = value;
}
}
}
现在您已经完成了这项工作,您可以通过传入接口的模拟实例并在该模拟上设置期望来测试逻辑的行为(由演示者表示)及其与视图的交互。
请注意,如果这一切看起来有点复杂和令人费解,那是因为它是!Winforms 的设计并没有真正考虑到这种事情 - 其他框架,如 WPF,并且使这一切变得更加容易。如果你能改变,我建议你考虑一下。
1我链接到的网页上的 Martin Fowler 已经放弃了这种模式,将其移动到两个更不同的模式中——我会保留这个术语,因为它是众所周知的。他的新被动视图与我一直以来对 MVP 的看法非常接近。