C# 关于对 Windows 窗体应用程序进行单元测试的建议

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9097044/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 05:55:22  来源:igfitidea点击:

Advice on Unit testing a Windows Forms application

c#winformsunit-testing

提问by DukeOfMarmalade

I have written a Windows Forms application and now I want to write some unit tests for it (not exactly test driven development seeing as I am writing the tests after I have developed but better late then never!) My question is that with such an application how do you go about writing the unit tests, given that nearly all of the methods and events are private? I have heard of NUnit Forms but I hear good and bad things about it, also there has been no real development on that project for a while so it looks abandoned. Also is it generally accepted that the project have have adequate unit testing in place if I wrote unit test cases for all of the events that a user would trigger by clicking/ pressing buttons, or would I have to go and write unit test cases for all methods and figure out a way to test my private methods?

我已经编写了一个 Windows 窗体应用程序,现在我想为它编写一些单元测试(不完全是测试驱动的开发,因为我在开发之后编写测试,但最好迟到永远不会!)我的问题是这样的应用程序考虑到几乎所有的方法和事件都是私有的,你如何编写单元测试?我听说过 NUnit Forms,但我听说过它的好坏,而且该项目已经有一段时间没有真正的开发了,所以它看起来已经被废弃了。如果我为用户通过单击/按下按钮触发的所有事件编写单元测试用例,或者我是否必须为所有事件编写单元测试用例,那么是否普遍认为该项目已经进行了足够的单元测试?方法并找出一种方法来测试我的私有方法?

EDIT:My business logic is seperated from my presentation logic, there is 1 or 2 public methods my business logic exposes so the form can access them, but what about all the private methods that are in the business logic?

编辑:我的业务逻辑与我的表示逻辑分开,我的业务逻辑公开了 1 或 2 个公共方法,以便表单可以访问它们,但是业务逻辑中的所有私有方法呢?

采纳答案by ziesemer

The first thing I would do is to ensure that you have proper separation of your business logic from your form. Basically, using an MVCpattern. Then, you can easily test everything outside the form, as if the form didn't even exist.

我要做的第一件事是确保您将业务逻辑与表单正确分离。基本上,使用MVC模式。然后,您可以轻松地测试表单之外的所有内容,就好像表单根本不存在一样。

Now, this could still leave some untested form-specific functionality. I.E., is the form wired-up to the service correctly? For this, then you could still consider something like NUnit Forms or another alternative.

现在,这仍然可能会留下一些未经测试的特定于表单的功能。IE,表单是否正确连接到服务?为此,您仍然可以考虑使用 NUnit Forms 或其他替代方案。

回答by jgauffin

Break out all business logic into a separate project and unit test that. Or at least move all logic from the forms into separate classes.

将所有业务逻辑分解为一个单独的项目并对其进行单元测试。或者至少将所有逻辑从表单移动到单独的类中。

回答by Daniel Mann

You have a few options.

你有几个选择。

  1. Use a tool like Coded UI to test via your user interface. This isn't a great option, because it's slower than unit testing and the tests tend to be more brittle.

  2. Separate your business logic from your presentation logic. If you have a lot of private methods performing business logic in your UI, you've tightly coupled your business logic to your presentation. Start identifying these and moving them out to separate classes with public interfaces that you can test. Read up on SOLIDprinciples, which can help you keep your code loosely coupled and testable.

  1. 使用 Coded UI 之类的工具通过您的用户界面进行测试。这不是一个很好的选择,因为它比单元测试慢,而且测试往往更脆弱。

  2. 将业务逻辑与表示逻辑分开。如果您有很多私有方法在您的 UI 中执行业务逻辑,那么您已经将业务逻辑与您的演示文稿紧密耦合。开始识别这些并将它们移到带有可以测试的公共接口的单独类中。阅读SOLID原则,它可以帮助您保持代码松散耦合和可测试。

回答by Wouter de Kort

The key to Unit Testing graphical applications is to make sure that all most all of the business logic is in a separate class and not in the code behind.

单元测试图形应用程序的关键是确保所有大多数业务逻辑都在一个单独的类中,而不是在后面的代码中。

Design patterns like Model View Presenterand Model View Controllercan help when designing such a system.

Model View PresenterModel View Controller等设计模式可以在设计这样的系统时提供帮助。

To give an example:

举个例子:

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";
    }
}

As you can see, the Form only has some infrastructure code to thy everything together. All your logic is inside your Presenter class which only knows about a View Interface.

正如您所看到的,表单只有一些基础结构代码可以将您的所有内容放在一起。你所有的逻辑都在你的 Presenter 类中,它只知道一个视图接口。

If you want to unit test this you can use a Mocking tool like Rhino Mocksto mock the View interface and pass that to your presenter.

如果您想对此进行单元测试,您可以使用类似Rhino Mocks的 Mocking 工具来模拟 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);
}

回答by llewellyn falco

Unit testing the View is simple enough using approvaltests ( www.approvaltests.com or nuget). there is a video here: http://www.youtube.com/watch?v=hKeKBjoSfJ8

使用批准测试(www.approvaltests.com 或 nuget)对视图进行单元测试非常简单。这里有一个视频:http: //www.youtube.com/watch?v=hKeKBjoSfJ8

However, it also seems like you are worried about making a function default or public for the purposes of being able to test functionality.

但是,您似乎也担心为了能够测试功能而将函数设为默认或公开。

These are usually referred to as seams; ways to get into your code for testing. and they are good. Sometime people confuse private/public with security, and are afraid to turn a private function public, but reflection will call either, so it's not really secure. Other times people are worried about the API interface to a class. But this only matters if you have a public API, and if you have a winform app, it is probably meant to be the top level (no other consumers are calling it.)

这些通常被称为接缝;进入代码进行测试的方法。他们很好。有时人们将 private/public 与安全性混淆,并且害怕将私有函数变为 public,但反射会调用任何一个,所以它并不是真正安全的。其他时候,人们担心类的 API 接口。但这仅在您拥有公共 API 时才重要,并且如果您有一个 winform 应用程序,它可能意味着是顶级(没有其他消费者调用它)。

You are the programmer, and as such can design your code to be easy to test. This usually means little more than changing a few methods public and creating a few connivence methods that allow dependences to be passed in.

您是程序员,因此可以设计易于测试的代码。这通常意味着更改一些公共方法并创建一些允许传递依赖项的便利方法。

For example:

例如:

buttonclick += (o,e)=> {/*somecode*/};

is very hard to test.

很难测试。

private void button1_Click(object sender, EventArgs e) {/*somecode*/}

still hard to test

仍然难以测试

public void button1_Click(object sender, EventArgs e) {/*somecode*/}

easier to test

更容易测试

private void button1_Click(object sender, EventArgs e) { DoSave();}
public void DoSave(){/*somecode*/}

Really easy to Test!

真的很容易测试!

This goes double if you need some information from the event. ie.

如果您需要事件中的一些信息,这会加倍。IE。

public void ZoomInto(int x, int y)

is much easier to test that the corresponding mouse click event, and the passthrough call can still be a single ignorable line.

更容易测试相应的鼠标单击事件,并且传递调用仍然可以是单个可忽略的行。

回答by user1040323

One may employ the MVVM (Model–View–ViewModel) pattern with Reactive.UI, to author testable WinForms code. To get the separation of concerns really need. See: Reactive.UI https://reactiveui.net/The main downside of using Winforms / MVVM / Reactive.UI is that there are not a lot of examples of its use (for WinForms). The upside is that it is applicable to just about all desktop frameworks and languages. You learn it for one, but the principles apply for all. When you have lots of private methods, that's OK. IMHO: try to use public methods to begin a business process you want to test. You can use tell-don't-ask: https://martinfowler.com/bliki/TellDontAsk.htmland still keep all those methods private.

可以使用带有 Reactive.UI 的 MVVM(模型-视图-视图模型)模式来编写可测试的 WinForms 代码。为了得到真正需要的关注点分离。请参阅:Reactive.UI https://reactiveui.net/使用 Winforms / MVVM / Reactive.UI 的主要缺点是没有很多使用示例(对于 WinForms)。好处是它几乎适用于所有桌面框架和语言。你只学一个,但原则适用于所有人。当你有很多私有方法时,那没关系。恕我直言:尝试使用公共方法来开始您要测试的业务流程。您可以使用 tell-don't-ask:https: //martinfowler.com/bliki/TellDontAsk.html并且仍然将所有这些方法保密。

One may also test the code by driving the UI but this is not so highly recommended, because the resultant tests are (1) very fragile, (2) harder to get working, and IMHO, (3) can't be written at the same level of fine granuality as pure code tests; (4) Finally: if you use a database, you will need to consider populating it with test data, and, because your database must be in a clean, well-defined state before each test, (5) your tests may run even slower than your thought as you reinitialize the data for each test.

也可以通过驱动 UI 来测试代码,但这不是强烈推荐的,因为由此产生的测试 (1) 非常脆弱,(2) 更难工作,恕我直言,(3) 不能在与纯代码测试具有相同级别的细粒度;(4) 最后:如果你使用一个数据库,你需要考虑用测试数据填充它,而且,因为你的数据库在每次测试之前都必须处于干净的、定义良好的状态,(5) 你的测试可能运行得更慢比您重新初始化每个测试的数据时的想法。

Summary: Author your code with good SoC (e.g. by applying MVVM), then your code will have far better testability.

总结: 用好的 SoC 编写你的代码(例如通过应用 MVVM),那么你的代码将具有更好的可测试性。