TDD与单元测试的区别

120

我的公司在进行单元测试时比较新。我一直在阅读有关TDD和单元测试的文章,深信它们的价值。我尝试说服我们的团队,TDD值得学习和改变我们编程思维,但却很困难。这就带来了我的问题。

TDD社区中有许多人非常坚持先编写测试再编写代码(我也是),但对于一个正在努力理解TDD的团队来说,妥协还能带来额外的好处吗?

我可能可以让团队在编写代码后编写单元测试(作为检查代码的要求),我的假设是编写这些单元测试仍然具有价值。

如何最好地让团队接受TDD?如果失败,即使在代码编写后编写单元测试,是否仍然值得?

编辑

我从中得出的结论是,我们需要在编码过程中开始进行单元测试。对于那些理解这个概念的团队成员,应该开始更加倾向于TDD和先测试的编程方式。谢谢大家的建议。

后续

我们最近启动了一个新的小项目,团队的一小部分使用了TDD,其余的在编写代码后编写了单元测试。项目的编码部分结束后,那些在编写代码后编写单元测试的人惊讶地发现TDD编码者已经完成并且具有更加稳定的代码。这是赢得怀疑者的好方法。我们仍然有很多成长的痛苦,但意见之争似乎已经结束。感谢每个人提供的建议!


1
你可能会发现这个线程有帮助:https://dev59.com/nnNA5IYBdhLWcg3wjOlS - Randolpho
30
+1 表示后续跟进。那个故事很棒。 - Carl Manaster
17个回答

76
如果团队在实现TDD方面遇到困难,但之前没有创建任何单元测试...那么,请让他们在编写代码后创建单元测试。即使是在代码编写后编写的单元测试也比没有单元测试好!一旦他们熟练掌握了单元测试(以及相关技术),然后再开始让他们先编写测试…再编写代码。

3
没错,团队以前没有创建任何单元测试。这感觉像是迈向完全TDD的好机会。 - Walter
完全同意这个观点。事实上,我几个月前为一个类似的问题写了类似的东西。它在哪里呢...啊!https://dev59.com/nnNA5IYBdhLWcg3wjOlS#917345 - Randolpho

27

在编写代码后编写单元测试仍然是有价值的。只是有时它可能更难,因为你的代码没有被设计成可测试的,而且你可能使它过于复杂。

我认为一个好的实用方法是在过渡期或长期内提供“开发期间测试”的另一种方法来引导团队进入TDD。可以鼓励他们使用TDD的自然部分。但是,在这些似乎很难以测试为先或使用非敏捷A&D流程预定的对象的代码部分中,开发人员可以选择编写一小段代码,然后编写覆盖该代码的测试,并重复此过程。与不编写任何单元测试相比,立即为某些代码编写单元测试要好。


16

在我看来,用 "先写代码,再写测试" 的方法实现 50% 的测试覆盖率并完成 100% 的库会比采用 TDD 方法实现 100% 的测试覆盖率但只完成了 50% 的库要好。过一段时间后,你的同行开发者们希望会发现为他们编写的所有 public 代码编写测试非常有趣和有益,这样 TDD 就会悄悄地融入他们的开发流程中。


3
我理解你的意思,但我对“100%完成的库”只有50%的测试覆盖率持谨慎态度......根据我的经验,每一段没有通过测试覆盖的代码中至少会包含一个错误。或者换个说法:人们往往会避免为那些需要更多测试的代码编写测试:) - Aaron Digulla
2
另一种说法是,已发布的有缺陷的代码比永远停滞不前的完美代码更好。显然有例外(NASA),但大多数情况下,请发布您的代码。发布后仍然可以添加测试。 - jcdyer
3
“100%完成的图书馆”是什么意思?如果存在漏洞,您是否认为它已完成?在“完成”的定义中,您不包括测试吗? - Pascal Thivent
10
经过测试并不意味着没有漏洞,这不足以作为缺陷无关的充分条件。 - fa.
2
测试覆盖率是由测试覆盖工具测量的双刃剑。如果通过调用IUT上的所有方法来实现,但测试并没有真正测试可能会出错的行为,开发人员和其他利益相关者将会有一种虚假的安全感。当关键行为未经测试而您拥有100%的测试覆盖率时,您组织内的整个TDD运动可能会失败。最重要的不是数量,而是质量。 - Doug Knesek
显示剩余5条评论

13

TDD关乎设计!如果您使用它,您将确保代码具有可测试的设计,从而更容易编写测试。如果您在编写代码之后编写测试,它们仍然有价值,但在我看来,您会浪费时间,因为您可能没有一个可测试的设计。

我可以给您的建议是尝试使用《无畏改变:引入新思想的模式》(Mary Lynn Manns和Linda Rising著)中描述的一些技术来说服您的团队采用TDD。


3
+1:测试驱动开发意味着设计是由测试考虑因素驱动的。 - S.Lott
+1. 当然,后面的单元测试会帮助你,但如果你不事先编写单元测试,就会失去拥有“可测试设计”的好处。 - Noufal Ibrahim

12
我刚在日历上看到这句话:“每条规则,如果执行到极致,都会变得荒谬甚至危险。”因此,我的建议是不要过分信奉单一的测试方法。每个团队成员都必须找到在测试方面感觉“正确”的平衡点。这样,每个团队成员将最有效地工作(而不是想着“为什么我要写这个烦人的测试”之类的想法)。
因此,有些测试比没有测试好,代码后的测试比少量测试好,而在编写代码前进行测试比代码后进行测试更好。但是每个步骤都有其自身的优点,即使是小的改进也不应被忽视。

作为一名开发者,我不记得曾经通过自己的感觉想过进行任何自动化单元测试,只能进行手动单元测试。我认为,对于测试缺乏兴趣并不是我的个例。 - Gennady Vanin Геннадий Ванин
@vgv8:这意味着你的测试对你没有帮助。这可能有很多原因,我建议深入挖掘。任何项目都受益于良好的测试,而受到不良测试的影响。当你开始编写好的测试时,你会注意到从那时起,没有什么能阻止你继续编写更多的测试了。 - Aaron Digulla
对我而言,感觉正确的测试级别应该覆盖编程单元的预期行为,从功能层面考虑:即用户通常进行的操作,包括某些人称之为“报告错误”的错误结果。如果确认存在错误,至少要编写一次测试!项目越大,团队越庞大,这一点就越重要。 - DaFi4

9
如果他们对测试不太熟悉,那么我建议先测试已经编写好的代码,然后逐步过渡到先编写测试。作为一个尝试学习TDD和单元测试的人,我发现完全改变我的思维方式,在编写代码之前编写测试有点困难,所以我采取的方法是一种50-50的混合;当我确切知道代码会是什么样子时,我会先编写代码,然后编写一个测试来验证它。对于我不太确定的情况,我会从测试开始,逆向思考。
此外,请记住,编写测试以验证代码,而不是编写代码以满足测试,并没有什么错。如果你的团队不想走TDD的路线,就不要强迫他们这样做。

6
一旦代码写完(也许作为提交代码的要求),我可以成功地让团队编写单元测试,我的假设是编写这些单元测试仍然有价值。
毫无疑问,单元测试的代码有价值(无论何时编写测试),我把“代码已经过单元测试”包括在“完成定义”中。人们可以使用TDD或不使用,只要测试即可。
关于版本控制,我喜欢使用“开发分支”,并采用“单元测试”政策(即代码编译和构建,所有单元测试通过)。当功能完成后,它们会从开发分支发布到主干。换句话说,主干分支是“完成分支”(主干上没有垃圾!),具有可发货政策(可以随时发布),比“单元测试”更严格,包括更多内容。

4
这是你的团队在开始相信之前必须有自己的成功经验。如果有人关心,我会发表一下我的nUnit顿悟:
大约5年前,当我在一个项目上工作时,我发现了nUnit。我们几乎完成了V1.0,并创建了一些测试来尝试这个新工具。由于我们是一个新团队,在紧张的期限、高期望(听起来熟悉吗?)等因素下,我们有很多漏洞(显然!)。无论如何,我们完成了1.0版本并开始进行1.1版本。我们重组了团队,我被分配了2名开发人员。我为他们做了一个1小时的演示,并告诉他们我们写的所有东西都必须有一个测试用例。在1.1版本的开发周期中,由于我们要编写更多的代码-单元测试,所以我们总是落后于其他团队。我们最终进入测试阶段时,我们的代码中完全没有错误。我们帮助其他人调试和修复它们的错误。在事后分析中,当漏洞数量出现时,它引起了每个人的注意。
我不傻到认为你可以通过测试获得成功,但当涉及到单元测试时,我是一个真正的信徒。该项目采用了nUnit,很快传播到公司的所有.NET项目中,这是由于1次成功的结果。我们的V1.1版本的总时间周期为9个开发周,因此这绝对不是一夜之间的成功。但从长远来看,它对我们的项目和为其构建解决方案的公司都取得了成功。

该项目采用了nUnit,很快在公司的所有.Net项目中得到了推广。如果一个产品/项目有C#,Java,C ++,SQL Server代码,应该怎么办? - Gennady Vanin Геннадий Ванин
我不知道...在部署到生产环境之前找到一种测试所有组件的方法?单元测试只是综合测试计划的一个方面,在其上线之前。您可以在任何情况下挖掘漏洞。 - No Refunds No Returns

4
毫无疑问,测试(首先、同时或甚至在之后)可以避免错误,提高生产力和信心。我建议采用它!我曾经面临类似的情况,因为我是一个“新手”开发者,在团队项目中工作时,常常会因为某个贡献破坏了构建而感到沮丧。我不知道是否该责怪自己,有时甚至不知道该责怪谁。但更让我担忧的是我是否也在给我的同事开发者造成同样的问题。这种认识促使我采取一些TDD策略。我们的团队开始制定一些愚蠢的游戏和规则,比如你不能回家直到所有的测试都通过了,或者如果你提交了没有测试的东西,那么你就必须为所有人买 “啤酒/午餐/等等”,这使得TDD更有趣。

3
如果你不先编写测试,那么它就不是“测试驱动”,只是测试而已。这本身就有好处,如果你已经有了一个代码库,为其添加测试肯定很有用,即使它不是TDD而仅仅是测试。
先编写测试是关注在编写代码之前代码应该做什么的过程。当然,你也会得到一个测试,这很好,但有人可能会认为这甚至不是最重要的点。
我会训练团队使用像Coding Dojo、Katas这样的玩具项目(见这里),并使用TDD(如果你能让有经验的TDD程序员参与这样的研讨会,那就更好了)。当他们看到收益时,他们会将TDD用于真实项目中。但同时不要强迫他们,如果他们看不到好处,他们就不会正确地使用它。

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