【问题标题】:What do you test with your unit tests?你用你的单元测试测试什么?
【发布时间】:2010-10-05 19:14:51
【问题描述】:

TDD 是近来每个人都在谈论的话题,我自己也尝试过一些,但我认为我不明白。我正在掌握如何编写单元测试,但我不明白我的单元测试应该测试什么。

  1. 如果我有一个返回数据列表的操作方法,我应该验证什么?只是视图名称正确,还是我也应该验证数据?
  2. 如果我也应该测试数据,我不会写两次相同的代码吗?如果我使用相同的方法来检索要比较的数据,那么测试数据有什么用?
  3. 我是否也应该测试添加/编辑数据的方法?如何验证是否以正确的方式添加/编辑/删除了记录?

我知道这是很多大问题,但我并没有通过阅读互联网上的文章变得更聪明,因为他们似乎都关心 如何 进行测试,而不是关心什么

举个例子——我有(或者,我要写)一个GuestbookController,有查看、添加、编辑和删除帖子的方法。我需要测试什么?我该怎么做?

【问题讨论】:

    标签: .net asp.net-mvc unit-testing tdd


    【解决方案1】:
    1. 对于给定的输入或状态,您应该测试方法的返回值是否符合您的预期。如果您的方法返回的数据类型很多,您应该验证它们都是正确的。
    2. 在您的测试用例中使用一小组样本数据。不要从磁盘或数据库加载任何内容。在您的测试中传入一个字符串或在那里构造测试对象。鉴于您希望从您的方法中获得非常具体的结果的那一小部分数据,您可以将其与实际结果进行比较。您希望避免在测试中为您计算值的代码过多,因为这样您就必须为您的测试编写测试以确保它们正常工作!
    3. 测试您编写的每一段代码。要添加记录(如果您的测试连接到测试数据库),您可以简单地查询最后插入的记录,或者比较前后的记录总数,并确保它增加了 1。如果您有一个模拟/存根框架,您可以跳过数据库并断言调用了将内容保存到数据库的方法。要测试编辑,只需再次从数据库中检索已编辑的记录并断言该值已从其旧值更改。或者,如果模拟/存根,则正确调用了更改属性值的方法。

    但实际上,为您即将编写的代码编写一个测试。看着它失败。然后编写足够的代码使其通过。现在编写另一个测试并重复。

    【讨论】:

      【解决方案2】:

      我认为您可以通过测试驱动开发/测试优先开发从单元测试中获得最大收益。您应该首先编写测试,然后编写通过测试的代码。你应该首先测试什么?

      我发现根据用户故事编写测试非常有用。大多数时候我都是从用户故事开始编写自上而下的风格测试。例如我们的用户故事包含这样一个任务:

      1. 当用户保存订单视图时应显示信息消息。

      我通常从表示/控制器层为这个故事编写测试

        [Test]
          public void When_User_Save_Order_View_Should_Display_Information_Message()
          {
              using (mockRepository.Record())
              {
                  repository.Save(order);
                  view.Message= "Order saved successfully";
              }
      
              using (mockRepository.Playback())
              {
                  presenter = new OrderSavePresenter(view, orderRepository);
                  presenter.Save();
              }
          }
      

      然后我继续为我模拟或需要的每个类编写测试。

      【讨论】:

        【解决方案3】:

        我不会费心测试简单的东西,比如 getter 或 setter。您不需要测试编译器本身,因此在调用 setter 时检查是否分配了值是没有用的。

        一般来说,我会尝试为具有实际代码的类的每个方法编写一个单元测试。这样,如果以后有人破坏了某个方法,它就会被捕获。

        例如,有人更改了“addElements(String[] elements)”之类的方法。他们试图使用“for(int i=0;i

        对于GuestBookController 之类的东西,可能有一些方法可以离线测试,但您可能需要实际建立与测试数据库服务器的连接。从那里开始,进行添加帖子、编辑帖子和删除帖子的测试,并使用检索帖子的方法验证每次发生的更改。

        单元测试应该让您对自己的代码工作更有信心,并且当您进行更改时它仍然有效。添加单元测试代码,直到您确信它已经过充分测试。在上面的示例中,您不必担心意外破坏留言簿。当您对代码进行更改时,单元测试会确认您仍然可以添加、删除、编辑和检索帖子。

        【讨论】:

        • 对于GuestBookController的单元测试,控制器应该真正使用模拟DAO并断言正确的交互已经发生。单元测试不应该命中数据库,这就是集成测试的工作
        【解决方案4】:

        单元测试 (UT) != 测试驱动设计 (TDD)

        这种混淆似乎相当普遍。 UT 是关于代码覆盖率的。 TDD 关注特性。它们是一回事[对不起乔尔!]

        使用 UT,您可以编写任何您想要的代码,然后返回并测试每个函数(甚至是一些微不足道的函数)。

        使用 TDD,您可以选择下一个功能并首先为该功能编写测试。只为该功能编写测试,测试覆盖率无关。您首先编写测试以强制预先做出接口决策。然后你编写代码以通过测试(记住“最简单的可能工作”)。然后根据所学重构代码。然后你继续下一个功能(大概是在签入并重新运行 all 单元测试之后)。

        如果需要,使用 TDD 进行开发,然后返回并使用 UT 工具完成覆盖。如果您正在创建类库或其他 API 供开发人员使用,测试覆盖率越高越好;-)

        如果您只是编写一个应用程序来做五件特定的事情,那么仅 TDD 就足够了。

        【讨论】:

        • 我同意 UT != TDD,因为 TDD 是一种使用 UT 的方法。但是,我不知道 TDD 文献中有任何定义支持您关于 UT 完全是关于覆盖范围的说法。请详细说明。
        • @[Brian Rasmussen]:UT 对代码覆盖率的重视来自两个方面——代码覆盖工具的供应商,以及被代码覆盖指标分散注意力(着迷)的人。 UT 本身是一种工具,而不是一种方法。在我读过的任何 TDD 文献中都没有提到代码覆盖率。
        • 通过单元测试,您不必测试每一个功能。例如,您不会测试没有逻辑的 getter 和 setter。但是,如果它们不仅仅是返回或设置一个值,那么它们确实是经过测试的。
        【解决方案5】:

        测试你正在测试的模块接口的契约

        • 如果客户希望在使用您的类时出现特定行为,请对其进行测试。
        • 如果您的类应该阻止客户端的某些行为(如其合同中定义的那样),请对其进行测试。

        我所说的客户端是指使用你的类的代码。
        预期行为是指方法对返回值和对象状态的预期结果。

        并将您的测试重点放在逻辑(if、for、while 等)上,因为像属性这样的扁平内容在不被应用程序的正常使用捕获的情况下失败的可能性较小。

        【讨论】:

          【解决方案6】:

          这些是我认为对单元测试有用的通用指南:

          1) 识别边界对象(Win/WebForms、CustomControls 等)。

          2)识别控制对象(业务层对象)

          3) 确保至少为边界对象调用的控制对象公共方法编写单元测试。

          这样您就可以确保您涵盖了应用的主要功能方面(特性),并且不会冒微测试的风险(除非您愿意)。

          【讨论】:

            【解决方案7】:

            在 TDD 中,您编写系统行为规范并使用它们来驱动系统设计。你为一个微小的行为编写一个测试,然后观察测试失败,然后编写代码以通过测试。通过定期重构,始终保持尽可能高的代码质量,以便更轻松地进行更多更改。

            如何进行 TDD 的示例: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata

            另请参阅Writing standards for unit testing 我的回答 - 它包含示例和链接以获取更多信息。这里也有很好的链接:http://code.google.com/p/instinct/wiki/UsersGuide

            【讨论】:

            • 我厌倦了现实世界中所有教条式的挥手致意。在您的第一个链接中,没有人对 Keith Gregory 的示例提供过令人满意的答案。事实上,我们的实现存在一些工程问题,这些问题不应该是公共 API 的问题。
            【解决方案8】:

            我认为这里有一点Shu-Ha-Ri。你在问一个很难解释的问题。只有在练习并努力应用 TDD 之后,您才能获得 What。在那之前,我们会给你一些没有意义的答案,以Monads Are Burritos 的精神告诉你一些事情。这对你没有帮助,而且我们听起来像白痴(monads 显然是柠檬雪纺派)。

            我建议获得 Kent Beck 的 TDD book 并完成它,然后只是练习。 “里没有王道。”

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-09-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-10-04
              • 1970-01-01
              相关资源
              最近更新 更多