单元测试的详细程度

Kar*_*gha 3 tdd junit nunit unit-testing

我想开始讨论你在单元测试中提到的细节.

您是否测试了主要功能,包括几种方法一次完成一项任务?或者你甚至可以测试自动属性?

因为,例如,我认为编写仅测试此测试的测试没什么价值:

   public Email
   {
      set
      {
         if(Regex.Match(/*....*/))
             email = value;
      }
      get
      {
        return email;
      }
   }
Run Code Online (Sandbox Code Playgroud)

因为它非常清楚,这只是浪费时间.通常当我进行单元测试时,我会测试整个任务 - 就像这个例子一样 - 整个注册过程.

我问这个是因为,目前我正在阅读Jimmy Nilsson撰写的"应用领域驱动设计和模式"一书,他在那里指出他正在用专门的测试测试这些小细节.

这种覆盖水平是否过度使用?

Sch*_*ern 12

测试不只是测试你写的东西.测试也可用于测试您编写的STILL的工作原理.比如,多年以及许多开发人员以后.你认为一个死的简单类可能会在未来变得更加复杂.

例如,假设它从没有过滤器开始.只是一个简单的获取/设置.为什么测试呢?然后,其他一些开发人员在正则表达式过滤器中添加了它的错误.然后突然班级被打破了,但是没有经过考验,所以没有人知道.这显示出调用堆栈进一步上升的神秘失败现在需要更多时间来调试.可能比编写一些测试要多.

然后,在将来,有人会尝试变得聪明并优化您的代码.或者重新格式化,正则表达式有一种不可读的倾向,并且通常可以进行一些清理.这对于微妙的破损是成熟的.小目标单元测试将抓住这一点.

在上面的示例中,正则表达式可能会过滤掉看起来像电子邮件地址的内容.这需要检查正则表达式是否正常工作,否则您的电子邮件类将停止接收电子邮件.或者它开始采取胡言乱语.也许你的正则表达式不包括有效的电子邮件地址,一旦你发现它就值得测试.最终,有人会用真正的解析器替换你的正则表达式.也许它有效,也许它没有.一个好的测试将有一个简单的有效和无效电子邮件地址列表,您可以轻松添加到这些地址,因为发现了极端情况.

测试还允许您锻炼界面并发现漏洞.当你输入不是电子邮件地址的东西时会发生什么?没错,没事.Email.set默默地抛弃输入.垃圾进去,没什么不礼貌的.也许它应该抛出异常.当你试图测试它时,这会迅速变得清晰,因为有必要测试该集是否有效.

测试还可以揭示无法覆盖或定制的不灵活性和事物.在您的示例中,直接测试正则表达式过滤器而不是每次都必须实例化对象会很方便.这是因为滤波器是最复杂的位,并且在通过尽可能少的层时更容易测试和调试.通过将其放入Email.is_email_address您可以直接测试它.作为副作用,它也可以在子类中重写.这很方便,因为大多数人都会收到错误的电子邮件验证,因为电子邮件会影响生活!

最后,您希望将测试分离.在不受其他复杂性影响的情况下测试一件事,这样你就可以清楚地看到问题的根源.您的电子邮件类是进行简单的解耦单元测试的绝佳选择.


小智 6

简单功能的测试本身很简单.因此它们易于编写且易于运行.它们只需要很少的时间来创建,并且是以后模块变得更复杂时的占位符.所以,是的,甚至测试简单的功能.

现在,您在上面键入的函数包含一个正则表达式,您可以对其进行有用的注释.正则表达式很难预测.匹配有效电子邮件地址的正则表达式可能非常复杂.所以我会测试它的地狱.在单元测试后,我会进行单元测试,使用我能想到的所有不同电子邮件地址变体的正则表达式,以及所有角落和负面情况.

关于是否编写测试的决定始终是关于短期和长期利益的决定.从长远来看,测试总是有益的,因为它们锚定代码的行为并检测副作用和意外后果.推迟测试的行为总是为了短期收益,这样你就不会"浪费时间".问题是,你不写的测试是系统中的一个漏洞,其中副作用和意外后果可能累积未被发现.由于简单模块的测试快速且易于编写,并且由于Murphy将确保隐藏的错误将隐藏在您为其提供的任何漏洞中,因此这些简单测试似乎不太可能真正浪费时间.