我已经编写了一个Windows窗体应用程序,现在我想为它编写一些单元测试(不完全是测试驱动的开发,因为我在开发之后编写测试但是迟到的时候从未好过!)我的问题是这样的应用程序如何编写单元测试,因为几乎所有的方法和事件都是私有的?我听说过NUnit Forms,但是我听到了它的优点和缺点,而且该项目暂时没有真正的发展,所以它看起来已经废弃了。此外,如果我为用户通过点击/按下按钮触发的所有事件编写单元测试用例,或者我必须为所有人编写单元测试用例,那么该项目已经进行了足够的单元测试方法并找出一种方法来测试我的私有方法?
编辑:我的业务逻辑与我的表示逻辑分离,我的业务逻辑公开了1或2个公共方法,因此表单可以访问它们,但是所有的私有方法呢?业务逻辑?
答案 0 :(得分:26)
单元测试图形应用程序的关键是确保所有业务逻辑都在一个单独的类中,而不是在后面的代码中。
设计此类系统时,Model View Presenter和Model View Controller等设计模式可以提供帮助。
举个例子:
public partial class Form1 : Form, IMyView
{
MyPresenter Presenter;
public Form1()
{
InitializeComponent();
Presenter = new MyPresenter(this);
}
public string SomeData
{
get
{
throw new NotImplementedException();
}
set
{
MyTextBox.Text = value;
}
}
private void button1_Click(object sender, EventArgs e)
{
Presenter.ChangeData();
}
}
public interface IMyView
{
string SomeData { get; set; }
}
public class MyPresenter
{
private IMyView View { get; set; }
public MyPresenter(IMyView view)
{
View = view;
View.SomeData = "test string";
}
public void ChangeData()
{
View.SomeData = "Some changed data";
}
}
正如您所看到的,Form只有一些基础设施代码可以将您的所有内容组合在一起。您的所有逻辑都在Presenter类中,它只知道View Interface。
如果您想进行单元测试,可以使用像Rhino Mocks这样的模拟工具来模拟View界面并将其传递给演示者。
[TestMethod]
public void TestChangeData()
{
IMyView view = MockRepository.DynamickMock<IMyView>();
view.Stub(v => v.SomeData).PropertyBehavior();
MyPresenter presenter = new MyPresenter(view);
presenter.ChangeData();
Assert.AreEqual("Some changed data", view.SomeData);
}
答案 1 :(得分:16)
我要做的第一件事就是确保您的业务逻辑与表单正确分离。基本上,使用MVC模式。然后,您可以轻松地测试表单外的所有内容,就好像表单甚至不存在一样。
现在,这仍然可能会留下一些未经测试的特定于表单的功能。 I.E.,是否正确连接到服务的表格?为此,您仍然可以考虑NUnit Forms或其他替代方案。
答案 2 :(得分:5)
将所有业务逻辑分解为单独的项目和单元测试。或者至少将表单中的所有逻辑移动到单独的类中。
答案 3 :(得分:5)
您有几个选择。
使用Coded UI等工具通过您的用户界面进行测试。这不是一个很好的选择,因为它比单元测试慢,而且测试往往更脆弱。
将业务逻辑与表示逻辑分开。如果您的UI中有许多私有方法执行业务逻辑,那么您的业务逻辑与您的演示文稿紧密相关。开始识别这些并将它们移出到可以测试的公共接口的单独类。阅读SOLID原则,这可以帮助您保持代码松散耦合和可测试。
答案 4 :(得分:0)
使用审核测试(www.approvaltests.com或nuget)对View进行单元测试非常简单。这里有一段视频:http://www.youtube.com/watch?v=hKeKBjoSfJ8
但是,为了能够测试功能,您似乎还担心将函数设置为默认值或公共函数。
这些通常被称为接缝;进入测试代码的方法。 而且他们很好。有时候人们会把私人/公众与安全混为一谈,并且害怕将私人功能公之于众,但是反思会调用,所以它并不是真的安全。其他时候人们担心类的API接口。但这只有你有一个公共API才有意义,如果你有一个winform应用程序,它可能是最高级别的(没有其他消费者在调用它。)
您是程序员,因此可以将代码设计为易于测试。这通常意味着只需更改一些公共方法并创建一些允许传递依赖关系的connivence方法。
例如:
buttonclick += (o,e)=> {/*somecode*/};
很难测试。
private void button1_Click(object sender, EventArgs e) {/*somecode*/}
仍然难以测试
public void button1_Click(object sender, EventArgs e) {/*somecode*/}
更容易测试
private void button1_Click(object sender, EventArgs e) { DoSave();}
public void DoSave(){/*somecode*/}
真的很容易测试!
如果您需要来自活动的一些信息,这将加倍。即
public void ZoomInto(int x, int y)
更容易测试相应的鼠标点击事件,并且直通调用仍然可以是单个可忽略的行。
答案 5 :(得分:0)
可以使用带有Reactive.UI的MVVM(Model-View-ViewModel)模式来编写可测试的WinForms代码。真正需要分离关注点。请参阅:Reactive.UI https://reactiveui.net/使用Winforms / MVVM / Reactive.UI的主要缺点是没有很多使用它的示例(对于WinForms)。好处是它几乎适用于所有桌面框架和语言。你可以学习它,但这些原则适用于所有人。当你有很多私人方法时,那没关系。恕我直言:尝试使用公共方法来开始您想要测试的业务流程。您可以使用tell-don&#t; t-ask:https://martinfowler.com/bliki/TellDontAsk.html并仍然将所有这些方法保密。
也可以通过推动用户界面来测试代码,但这不是强烈推荐的,因为最终的测试是(1)非常脆弱,(2)难以开始工作,恕我直言,(3)可以&#39;与纯代码测试一样,写出与细粒度相同的水平; (4)最后:如果你使用数据库,你需要考虑用测试数据填充它,并且,因为你的数据库必须在每次测试之前处于一个干净,定义良好的状态,(5)你的测试可能运行得更慢而不是你重新初始化每个测试的数据时的想法。
总结:使用优秀的SoC编写代码(例如,通过应用MVVM),那么您的代码将具有更好的可测试性。