在编写单元测试之前编写代码有什么缺点?


33

我一直看到建议,我们应该首先编写单元测试,然后再开始编写代码。但是我觉得(对我来说)走另一条路要舒适得多-编写代码,然后进行单元测试,因为在编写实际代码后,我觉得我们更加清楚了。如果我先编写代码然后进行测试,那么即使我将精力集中在创建可测试的设计上,也可能需要稍稍更改一下代码以使其可测试。另一方面,如果我先编写测试,然后编写代码,则当代码成形时,测试将非常频繁地更改。

正如我看到的有关开始编写测试然后继续进行编码的大量建议一样,如果我以其他方式进行编写,然后编写单元测试,则有什么缺点呢?


7
+1,询问在接受
某项

Answers:


37

红色是答案。红色是您无法从TDD的红色-绿色重构周期中获得的,最后一次测试。首先,编写一个失败的测试。观看失败。那是你的红色,这很重要。它说:我有这个要求,我知道我的代码不能满足要求。因此,当您转到第2步(绿色)时,就可以肯定地知道您的代码现在已满足该要求。您知道您已经按照满足要求的方式更改了代码库。

在基于代码的代码之后开发的需求(测试)剥夺了您这种确定性和信心。


+1-得分极高!感谢您的意见!
2011年

7
测试!=要求。测试和代码都应要求中得出
Bart van Ingen Schenau 2011年

2
@Bart van Ingen Schenau:TDD的优势正是测试ARE要求。而且,它们是可执行的要求。
mouviciel 2011年

1
@Bart:对于(高级)客户需求而言,单元测试通常过于详细,但是这种想法绝对成立,尤其是如果我们还考虑了更高级别的测试,例如自动验收测试,这些测试一旦编写,就应该成为最终要求。这就是“敏捷验收测试”的本质。
Martin Wickman

3
TDD与测试无关,而与规范有关。使用TDD方法构建的测试是开发人员和客户之间就应该生产哪种产品达成协议的一种交流手段。
mouviciel 2011年

18

如果先编写代码,然后编写测试,那么很容易陷入编写测试的陷阱,以使代码通过,而不是编写测试以确保代码符合规范。

也就是说,这绝对不是唯一的处理方法,也没有“最佳”的软件开发方法。如果您将大量的前期工作投入到开发测试用例中,那么直到很久以后您才知道所提议的体系结构是否存在缺陷-而如果您首先开发了代码,则可以更快地将其投入使用,并且可以减少沉没的情况下进行重新设计。努力。


是的,您对第一点是正确的,但我始终确保我不这样做。如果测试失败,我将始终转到代码并确保其正确,然后查看我的测试是否正确,然后修改任何错误的代码。感谢您的意见,我会谨记在心。.+1是我的第一点和最后一点...
k25 2011年

2
但是如果测试通过了怎么办?该测试可能会通过,因为它实际上没有在执行感兴趣的代码。在TDD下这实际上是不会发生的,因为该测试最初应该会失败,并且会失败-否则,除非您已解决问题,否则请不要继续执行步骤2。因此,在测试的最后一个失败模式中,测试的第一个并不存在。
2011年

@Carl Manaster-是的,您的观点确实正确。编写代码后,我完全了解需求,因此我的单元测试用例将是正确的(理想情况下)。如果我的测试用例通过,我会说代码是正确的,如果测试失败,我将按照我说的去做。但是,我100%同意您在那里有一个正确的论点。
2011年

@ k25:关键是,如果您的测试用例通过,您仍然不知道代码是否正确。测试用例可能是错误的。
Anon。

@Anon。-是的,您是对的,我也会考虑这种情况。
2011年

12

实际上,人们对TDD感兴趣的是测试,尽管他们忘记了首字母缩写词中的其他两个字母。可以在这里阅读的内容:没有TTDD的TDD与Testing无关

事情是,我学到了很多其他与TDD紧密相关的东西。不需要先进行测试就没关系:重要的是考虑软件设计

为了甚至能够以“正确的方式” 编写单元测试,即使它们独立,快速且自动化,您希望会注意到,它需要重新思考如何以简化代码的方式安排代码去测试。

我个人不了解SOLID原理,而是学会了SOLID原理。这是因为编写单元测试迫使我重写类,这样它们就不会变得过于复杂而难以测试。它导致类似:

  • 我不得不将没有意义或驻留在私有方法中的功能移到单独的类上,以便我可以分别对其进行测试。(单一责任原则)。
  • 我必须避免使用较大的继承结构,而应使用组合来扩展实现(在“开放式-封闭式”原则中很突出)。
  • 我必须对继承非常聪明,每当看到可以共享的通用代码并使用存根方法(Liskov替换原理)时,我就使用抽象类。
  • 我必须编写接口和抽象类,以便可以单独测试类。这无意间导致您编写模拟对象。(接口隔离原理)
  • 因为我写了很多接口和抽象类,所以我开始声明变量和参数以使用公共类型(依赖倒置原理)。

即使我不是一直都在进行测试,但我确实遵循了您开始遵循的良好的面向对象原则和实践,只是使测试变得更加容易。现在我不是为了自己而编写代码。我编写了代码,因此可以轻松对其进行测试或更重要的是,易于维护


1
考虑软件设计时,您自然会想到SOLID +1。
ocodo 2011年

+1(实际上我想给+10,但我不能)。正是我的想法-您列出的要点非常好。这就是我问这个问题的原因之一。当我在编写代码后开始编写单元测试时,我感到类变得更多了。但我想看看双方的优缺点,谢谢您的意见!
2011年

10

所有其他答案都是好的,但是有一点没有被提及。如果您首先编写测试,则可以确保编写测试。一旦编写了工作代码,就很想跳过测试并仅通过UI进行验证。如果您有一定的纪律在编写代码之前总是要通过失败的测试,则可以避免这种陷阱。


4

如果您首先编写测试,那么它会给您另一个机会来思考您的设计,然后再将其“付诸实践”。

例如,您可能认为您需要一种采用一组特定参数的方法。而且,如果您首先编写了代码,则将以这种方式编写,并使测试适合指定的参数。但是,如果您首先编写测试,您可能会想“等一下,我不想在主线代码中使用此参数,所以也许我应该更改API。”


+1为第一点。但是,如果没有达到参数级别,如果与他人讨论并接受了设计该怎么办?
2011年

@ k25-如果难以按设计使用某些东西,则需要更多的思考。有时-很少-这只是一项艰巨的任务。但是更多时候,它可以简化为更简单的任务。我没有链接,但是几年前Gosling或Goetz都接受了有关API设计的采访...值得Googling。
匿名

当然,感谢您的指点,我一定会关注他们的……
k25 2011年

2

我看到很多建议,开始编写测试,然后再进行编码,

这确实有充分的理由。

如果您说“做正确的事”,人们就会做最愚蠢,最疯狂的事情。

如果您说“首先编写测试”,那么人们至少可以尝试做正确的事情。

如果我用另一种方法做的话有什么缺点-​​编写代码然后进行单元测试?

通常,糟糕的测试和必须重新设计才能测试的设计。

但是,这只是“通常”。有人并行开发设计和测试。有些人将可测试的代码放在适当的位置,编写测试而无需返工。

“测试优先”规则专门用于教导和指导完全不了解的人。

以类似的方式,总是告诉我们过马路之前要“双向看”。但是,实际上我们没有。没关系。我住在右驾国家,开始穿越时只需要向左看。

当我访问左驾国家时,向左看只会使我丧命。

出于某些原因,对规则的说明非常严格。

您要做的是您自己的问题。


2

首先编写测试的要点是它使您思考

  • 如何测试代码
  • 代码必须提供可测试的接口

如果您做的是简单的事情,那么首先写哪个可能并不重要(尽管养成以测试为先的习惯是件好事),因为测试很简单,界面也很明显

但是TDD会扩展到验收测试,而不仅仅是单元测试,然后界面变得不平凡。


1

首先,如果您不首先编写测试,那么您就没有进行测试驱动开发(TDD)。好处是很多的,通常很难相信,除非您多次练习。与传统开发相比,以下是我在使用TDD时获得的好处:

  1. 测试的安全网-允许您进行重大更改,而不必担心会不小心破坏某些内容
  2. 有机设计-我最终得到的设计通常与我从头开始进行的设计有所不同,并且始终比以往更好
  3. 生产力-朝着小目标努力(通过一项测试)并使其实现(所有测试通过)对于我而言确实非常有效,并且使我保持动力。加上一对,我的生产率达到了新的高度。

书籍:Beck,K.通过示例进行测试驱动的开发

很好的例子:http : //jamesshore.com/Blog/Lets-Play/


+1-不错的积分(尤其是第一积分),感谢您的链接!
2011年

0

当您编写测试时,您如何知道它将检测到失败情况?答案是“测试测试”。这样做的方法是先编写测试,查看失败,然后仅在成功编码了被测单元(其他答案之一中提到的红色/绿色/重构周期)后才能通过测试。

首先编写代码,然后进行测试,这留下了一个问题,即测试是否会显示出诚实的失败。

请记住,您的测试表示规范。如果在代码“成形”时必须修改测试,则表明您的规范正在更改。那可能不是好事。这可能意味着您对问题的理解最初并不正确。在另一方面,它可能意味着你要测试“如何”的单位在做自己的工作,而不是什么是应该办成。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.