我们应该多久编写一次单元测试?

21

我最近在工作中的导师介绍下了解到了测试驱动开发的方法,并且他鼓励我在“有意义”的情况下编写单元测试。我理解对于回归测试和重构来说拥有一套完整的单元测试套件的一些好处,但我确实想知道我们应该多频繁、多详尽地编写单元测试。

我的导师/开发负责人要求我为一个已经由现有测试类测试的方法中新增的控制流编写新的单元测试用例,我认为这是过度设计。您通常编写单元测试的频率如何,您认为单元测试应该有多详细?谢谢!


这真的是TDD吗,还是只是单元测试? - quamrana
12个回答

25

技术上讲,如果你严格遵循测试驱动开发(TDD)的方法,那么在规划应用程序的任何部分之后,你都应该编写单元测试。应该按以下顺序进行:

用户需求 -> 功能设计 -> 单元测试 -> 代码

记住,在TDD中测试先于代码


这里有一个漂亮的插图:http://blog.typemock.com/2008/12/starting-test-driven-development-using.html - Gad
如果你遵循严格的测试驱动开发,代码是在一个循环中产生的:测试(单数)-> 代码 -> 重构 -> 另一个测试,而不是在编写所有测试之后再编写代码。 - quamrana

5

每当你编写任何代码时,都应该编写单元测试。正如其他人指出的那样,在TDD中,你在编写代码之前编写测试。

如果你认为这是过度的,那就问问自己:“如果我不测试这段代码,我怎么知道它能正常工作呢?”


唯一的问题是编写测试也不能告诉你它是否有效,只能告诉你它通过了你想到的任何测试。 - darron
@darron 这就是为什么你要基于功能设计来进行测试。测试告诉你它是否符合要求。 - Jack Ryan
1
... 但是关键是,如果你没有为刚添加的代码块编写测试,那么你无法知道它是否有效。至少,如果你编写了测试,你可以证明它按照某些规范(即测试所检查的规范)工作。 - Bryan Oakley
1
@darron 我不同意你的观点:如果你只编写符合测试要求的代码,而且没有其他额外的内容,那么你的测试就成为了规范的定义。这意味着你不仅仅有“你想到的”测试......你的测试定义了你的代码应该做什么,而你的代码确实也只做了这些!你的评论更适用于事后编写单元测试的情况:在那种情况下,你真的没有你想要的安全性。 - Epaga
1
@Epaga:抱歉,这太荒谬了。假设一个开发者是一名新手,从未听说过某些语言环境在数字字段中颠倒“,”和“。”。即使先编写测试,然后只编写符合测试的代码……他的所有测试都将通过!这仍然是一个错误,当这种情况出现时,你不可能期望告诉客户“它按规定工作”。这是一个简单的例子问题,但深思熟虑意味着TDD根本不可能让你“知道”任何东西……除了简单地事实,它似乎通过了你想到的测试,并且你希望正确地编写了这些测试。 - darron
显示剩余6条评论

2
我的导师/开发领导要求我为一个新编写的控制流编写一个新的单元测试用例。
那个控制流是怎么被编写出来的呢,除非它是为了通过一个失败的测试而被需要的?根据TDD的定义,除非存在需要编写那段代码的失败测试,否则不会有任何生产代码存在。所以你一定是在使用某种测试后编写代码的技术而不是TDD。
我建议你阅读文章《敏捷开发艺术:测试驱动开发》。你可以使用这个教程来练习TDD。
我认为你的导师使用“只要有意义就可以”这个短语可能会有害,特别是对于新接触测试驱动开发的人来说,因为在达到Ri-level之前,一个人不可能做出好的决定,需要多年的经验。当{{link2:Kent Beck决定编写测试}时,Ron Jeffries适当地评论道:“我相信你和其他三个人能做出好的短期决策。”
你应该始终首先编写测试。任何可能会出错的东西都需要测试。只有那些永远不会出错的东西,因为有人更改了代码而不需要测试。例如,声明性代码很少出错:Web页面上的静态HTML布局代码通常不值得自动测试(您必须手动测试以查看其是否正确),但任何动态内容都值得测试。

1

单元测试应该让你有信心,你所编写的代码能够按照你的意愿运行。因此,你应该编写尽可能多的测试来获得这种信心。


1

编写测试,尤其是可测试的代码,是一种独立的技能,就像学习每种新语言编程一样,都是一种独立的技能。

阅读 Miško Hevery 的博客,读一些书籍(我喜欢 Lasse Koskela 的 Test Driven),使用一些代码覆盖工具,如Clover,然后编写一些测试。

随着您编写测试,您的测试技能将得到改善,并且您将逐渐理解编写可测试代码所涉及的内容。然后,您将能够确定特定项目所需的代码覆盖水平。


1

我最近也成为了TDD的信徒,发现它非常有帮助。其核心思想是一旦你有了规范,就开始编写单元测试。一旦你编写了测试,你的大部分代码都可以来自这些测试。剩下的就是你测试中的断言基础,然后你就有了一个非常好的可重复模式:编写测试、实现代码、通过断言进行确认、继续。真是太棒了!


0

正如Justin所说,你应该在编写代码之前编写单元测试。

虽然这可能是最好的做法,但有时会过度。根据项目的不同,我有时仅为每个发现(并解决)的错误编写一个单元测试。

当然,我可能会错过其中一些,但随着时间的推移,我的单元测试变得越来越有效,并且我非常确定我永远不会错过已经纠正的错误。


0

单元测试将会:

  • 让你有信心进行重构更改,同时知道你没有破坏其他任何东西。
  • 作为您代码的“活文档”,让其他人知道代码在各种情况下的行为。
  • 当遵循@Justin所描述的严格TDD时,通过在编写代码之前编写的所有单元测试,可以让您知道何时完成编码。根据我的经验,这也会导致大大简化和更易于理解(更清洁)的代码。

正如您的导师所提到的,您应该只编写有意义的单元测试。我使用的经验法则是,尝试为执行业务逻辑的代码片段编写单元测试。我通常不为仅具有getter和setter的bean类编写单元测试。

随着时间的推移,单元测试通常变得更有价值,因为它们被扩展和增强以处理初始交付后进行的错误修复和其他代码增强。


0

在进行测试驱动开发时,单元测试的一个基本原则是它们描述功能。如果测试没有定义如何处理 null 输入等内容,那么就不能期望其能够正常工作。


0

如果您现有的测试套件不足,则应始终编写测试。最好的迹象是,您的测试不足会导致出现错误。因此,一个好的实践是为遇到的每个错误编写单元测试。使用测试驱动开发,您应该从一些测试开始,定义类的功能。测试覆盖工具可以为您提供一些提示,哪些部分可能需要更多的测试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接