3

我知道在您实际编写代码之后编写测试并不是那么好。我是单元测试新手,觉得单元测试可以带来很多好处,所以我痴迷于尽可能多地覆盖的想法。

例如,让我们有这样的代码:

public class ProjectsPresenter : IProjectsViewObserver
{
    private readonly IProjectsView _view;
    private readonly IProjectsRepository _repository;

    public ProjectsPresenter(IProjectsRepository repository, IProjectsView view)
    {
        _view = view;
        _repository = repository;
        Start();
    }

    public void Start()
    {
        _view.projects = _repository.FetchAll();
        _view.AttachPresenter(this);
    }

}

所以看看上面的代码,你能回答我通常应该在上面的那段代码上写什么测试吗?

我正在对构造函数进行写入测试,以确保调用了存储库的 FetchAll 并在视图站点上调用了 AttachPresenter。


后编辑

这是我的视图界面:

public interface IProjectsView
{
    List<Project> projects { set; }
    Project project { set; }

    void AttachPresenter(IProjectsViewObserver projectsPresenter);
}

这是一个视图:

public partial class ProjectsForm : DockContent, IProjectsView
{
    private IProjectsViewObserver _presenter;
    public ProjectsForm()
    {
        InitializeComponent();
    }

    public Project project
    {
        set
        {
            listBoxProjects.SelectedItem = value;
        }
    }

    public List<Project> projects
    {
        set
        {
            listBoxProjects.Items.Clear();   
            if ((value != null) && (value.Count() > 0))
                listBoxProjects.Items.AddRange(value.ToArray());
        }
    }

    public void AttachPresenter(IProjectsViewObserver projectsPresenter)
    {
        if (projectsPresenter == null)
            throw new ArgumentNullException("projectsPresenter");

        _presenter = projectsPresenter;
    }

    private void listBoxProjects_SelectedValueChanged(object sender, EventArgs e)
    {
        if (_presenter != null)
            _presenter.SelectedProjectChanged((Project)listBoxProjects.SelectedItem);
    }
}

编辑后 #2

这就是我测试与存储库交互的方式。一切都顺利吗?

    [Test]
    public void ProjectsPresenter_RegularProjectsProcessing_ViewProjectsAreSetCorrectly()
    {
        // Arrange
        MockRepository mocks = new MockRepository();
        var view = mocks.StrictMock<IProjectsView>();
        var repository = mocks.StrictMock<IProjectsRepository>();
        List<Project> projList = new List<Project> {
            new Project { ID = 1, Name = "test1", CreateTimestamp = DateTime.Now },
            new Project { ID = 2, Name = "test2", CreateTimestamp = DateTime.Now }
        };
        Expect.Call(repository.FetchAll()).Return(projList);
        Expect.Call(view.projects = projList);
        Expect.Call(delegate { view.AttachPresenter(null); }).IgnoreArguments();
        mocks.ReplayAll();
        // Act
        ProjectsPresenter presenter = new ProjectsPresenter(repository, view);
        // Assert
        mocks.VerifyAll();            
    }
4

5 回答 5

2

我知道在你真正写完代码之后再写测试不太好

总比不写测试要好。

您的方法适用于两个外部组件,并且应该验证交互(除了提到的参数验证)。检查是否FetchAll被调用没有给你任何价值(或检查它返回一些东西- 这属于ProjectsRepository测试本身) - 你想检查视图的项目是否已设置(这将间接检查是否FetchAll被调用)。您需要的测试是:

  • 验证视图项目是否设置为预期值
  • 验证演示者是否已连接
  • 验证输入参数

编辑:您将如何测试第一个案例的示例(项目已设置)

// "RegularProcessing" in test name feels a bit forced;
// in such cases, you can simply skip 'conditions' part of test name
public void ProjectsPresenter_SetsViewProjectsCorrectly()
{
    var view = MockRepository.GenerateMock<IProjectView>();
    var repository = MockRepository.GenerateMock<IProjectsRepository>();
    // Don't even need content;
    // reference comparison will be enough
    List<Project> projects = new List<Project>();
    // We use repository in stub mode;
    // it will simply provide data and that's all
    repository.Stub(r => r.FetchAll()).Return(projects);
    view.Expect(v => v.projects = projects);

    ProjectsPresenter presenter = new ProjectsPresenter(repository, view);

    view.VerifyAllExpecations();
}

在第二种情况下,您将对AttachPresenter使用有效对象调用它的视图设置期望:

public void ProjectsPresenter_AttachesPresenterToView()
{
    // Arrange
    var view = MockRepository.GenerateMock<IProjectView>();
    view.Expect(v => v.AttachPresenter(Arg<IProjectsViewObserver>.Is.Anything));
    var repository = MockRepository.GenerateMock<IProjectsRepository>();

    // Act
    var presenter = new ProjectsPresenter(repository, view);

    // Assert
    view.VerifyAllExpectations();
}
于 2012-05-01T07:36:13.397 回答
0

我会在 start 添加简单的测试,例如:

  • null参考检查

  • FetchAll()返回任何值

不要在第一次添加大量测试代码,而是在您的开发代码发生变化后对其进行改进。

于 2012-05-01T06:14:45.000 回答
0

我会添加异常测试,例如 ArgumentException、极端情况和 FetchAll() 的常见情况。

PS。开始必须是公开的吗?

于 2012-05-01T06:19:22.373 回答
0

Pex 是一个有趣的工具,值得一试。它可以生成具有高代码覆盖率的单元测试套件:http ://research.microsoft.com/en-us/projects/pex/ 。它不会取代你自己对代码的了解——以及哪些测试场景对你来说比其他人更重要——但它是一个很好的补充。

于 2012-05-01T07:44:39.400 回答
0

在编写生产代码之前编写测试的目的首先是让您(开发人员)进入“我如何知道我的代码何时工作?”的思维模式。当您的开发专注于工作代码的结果而不是代码本身时,您将专注于您的代码获得的实际业务价值,而不是无关紧要的问题(已经花费了数百万工时来构建和维护用户从未要求、想要或需要)。当你这样做时,你正在做“测试驱动开发”。

如果你在做纯 TDD,答案是 100% 的代码覆盖率。也就是说,您不会编写一行单元测试代码尚未涵盖的单行生产代码。

In Visual Studio if you go to Test->Analyze Code Coverage it will show you all of the lines of code that you do not have covered.

Practically speaking its not always feasible to enforce 100% code coverage. Also there are some lines of code that are much more important then others. Determining which depends once again on the business value provided by each line and the consequence of that line failing. Some lines (like logging) may have a smaller consequence then others.

于 2017-03-20T20:55:59.413 回答