2

我同意 Osherove 的观点,即单元测试应该有一个断言,而不是很多。但是,当我将该主体应用于 ASP.NET MVC 单元测试时,我想知道它是否过于复杂。考虑这些测试:

    [TestMethod] 
    public void RedirectTest() {     
        // Arrange - create the controller   
        ExampleController target = new ExampleController();     

        // Act - call the action method     
        RedirectResult result = target.Redirect();   

        // Assert - check the result
        Assert.IsFalse(result.Permanent);     
        Assert.AreEqual("/Example/Index", result.Url); 
    }

    [TestMethod]
    public void RedirectValueTest() {     
        // Arrange - create the controller     
        ExampleController target = new ExampleController();     
        // Act - call the action method     
        RedirectToRouteResult result = target.Redirect();     
        // Assert - check the result     
        Assert.IsFalse(result.Permanent);     
        Assert.AreEqual("Example", result.RouteValues["controller"]);    
        Assert.AreEqual("Index", result.RouteValues["action"]);    
        Assert.AreEqual("MyID", result.RouteValues["ID"]); 
    }

当然,上面的两个测试可以分成六个测试(每个断言一个),但感觉有点过分了。有没有围绕这个的最佳实践?你做什么工作?如果这是测试专家共识的所在,我肯定会沿着每个断言进行一次测试的路径...

4

5 回答 5

2

The first issue I see is your test names ... they don't say what the unit test is really supposed to be testing. I would break some of those tests up. This first test would remove the Assert.IsFalse(result.Permanent) from both your tests:

[TestMethod] 
public void Redirect_result_should_set_Permanent_to_false() {     
    // Arrange - create the controller   
    ExampleController target = new ExampleController();     

    // Act - call the action method     
    RedirectResult result = target.Redirect();   

    // Assert - check the result
    Assert.IsFalse(result.Permanent);     
}

Why should you do this? If result.Permanent == true, both your tests will fail, but you won't know why. In this case, you will know why.

于 2012-04-20T18:32:12.337 回答
2

断言
你的单元测试应该只测试一个集中的场景。这可能是也可能不超过一个实际的物理断言。最好它只是一个物理断言,但这并不总是可能的。它的要点是,每个测试只涵盖一个特定的重点场景。

命名和组织
命名你的单元测试是非常重要的。你的名字应该反映你正在测试的场景。

例如,您可以让多个测试类测试一个物理类。这也是一种很好的做法,因此您可以将测试分解为相关的组,并且不会用太多行代码填充单个测试类,从而变得不可读。

我之前看到的是开发人员使用正在测试的类的名称创建一个测试文件夹。该文件夹包含每个逻辑测试组的测试类。在每个课程中,您都有几乎听起来像句子的方法。

假设您要测试的类被调用MyCalculator,因此您创建了一个名为MyCalculator.Tests. 在该文件夹中,您现在可以为每组逻辑测试创建一个测试类。比如一个名为MultiplicationTestsor DivisionTestsor的类AdditionTests等等。每个类都可以包含与该逻辑组相关的测试方法。

例如,对于添加测试,您可能有测试方法,例如

  • When_adding_any_2_numbers_then_result_is_correct
  • When_adding_small_negative_and_large_positive_numbers_then_result_is_correct_positive
  • When_adding_large_negative_and_small_positive_numbers_then_result_is_correct_negative

每个测试只测试一个给定的场景,测试的名称也很好地描述了它。

你为什么要把你的场景分解成这么多的测试?通过这样做,您可以确保您很可能只在每个单独的测试中测试一个重点场景。您还可以确保将来发生错误时,只有直接影响的测试才会中断。

例如,如果您在某个阶段有一个错误导致您的 add 方法在添加的结果为负数时抛出异常,那么只有与测试负结​​果有关的测试或测试会受到影响。

此外,如果您的测试断言了如此多的场景,您实际上并不知道究竟是什么导致了测试失败,那么您花在编写重点单元测试上的时间远远超过了后期所涉及的成本和时间。

组织测试的方法有很多,但总体目标应该始终相同,即每个测试只测试一个特定的重点场景。

您的场景
在您的场景中,例如在您RedirectTest的测试中,如果失败,则将Assert.IsFalse(result.Permanent);失败。这并不一定意味着您的重定向失败。只有当您没有被重定向到预期的 URL 时,您的测试才会失败。如果页面内容未按预期呈现或数据丢失,则与该特定测试无关。

于 2012-04-20T20:10:29.890 回答
0

就我个人而言,我倾向于在每个测试中保留一个断言。当我测试 Asp .Net MVC 控制器时,我有时会在单个测试中保留多达四个(我个人的经验法则)断言。

每个测试有多个断言的问题是,当第一个断言失败时,您将不知道其余断言是否有任何问题,因此很可能会发生您修复代码以便首先断言通过只是为了发现第二个失败的原因不同。

有时,即使您在控制器中测试一些非常具体的东西,如果结果包含正确的视图名称,并且关联的模型包含预期的数据,那么在我看来,不止一个断言是有意义的。在这种情况下,您可以在一个测试上放置多个断言,充分了解该方法的缺点,或者您可以在一个方法中提取测试的排列和操作部分,并创建几个调用该方法的测试,获取结果并断言特定的数据。

于 2012-04-23T19:29:35.680 回答
0

只要测试一个离散的代码路径,它就可以有尽可能多的断言。每个测试只有一个断言似乎在维护测试方面有点过分了。

于 2012-04-20T17:56:08.790 回答
-1

一些人说你应该只有一个断言的主要原因(?)是因为你一次只能从一个断言中获得失败反馈。也就是说,如果您将断言放在不同的测试中,您将获得更多反馈。

我认为每个测试可以有几个断言。还有其他更重要的方面,例如可读性。如果你有更多的测试,你会得到更多的代码来阅读,并且更难获得你的测试套件的概览。

但是,我认为所有重要的断言都应该放在测试中,这样您就不必去其他地方找到它,从而使其更难掌握。重要的是我的意思是测试旨在验证的东西。例如,您可以对必须为测试有意义的事情以及其他测试更清楚地验证的事情隐藏断言。

于 2012-04-20T18:48:11.303 回答