是否应该在编写代码之前编写单元测试?

时间:2008-10-29 14:49:28

标签: unit-testing tdd

我知道测试驱动开发的一个定义原则是你首先编写单元测试,然后编写代码来通过这些单元测试,但这是否有必要这样做?

我发现在我写这篇文章之前,我经常不知道自己在测试什么,主要是因为过去几个我参与过的项目都是从概念验证而不是设计而来的。

我之前尝试过编写单元测试,它可能很有用,但对我来说这似乎并不自然。

20 个答案:

答案 0 :(得分:35)

这里有一些好的评论,但我认为有一件事被忽略了。

编写测试首先会推动您的设计。这是重要的一步。如果您“同时”或“很快”编写测试,您可能会遗漏一些微步骤TDD的设计优势。

一开始感觉真的很俗气,但是在你眼前看到的东西展现出你原本没想到的设计真是太神奇了。我见过它。

TDD很难,并不适合所有人。但是,如果您已经接受了单元测试,那么请试用一个月,看看它对您的设计和工作效率有何影响。

您在调试器中花费的时间更少,而且更多时间考虑外部设计。这是我书中的两个巨大的优点。

答案 1 :(得分:7)

已经有studies表明在编写代码之后编写的单元测试是更好的测试。但需要注意的是,人们不会在事件发生后写下这些内容。所以TDD是一个很好的妥协,因为至少测试得到了。

因此,如果你在编写代码后编写测试,那对你有好处,我建议你坚持下去。

我倾向于发现我做了一个混合物。我越了解这些要求,我就可以写出更多的测试。当要求 - 或者我对问题的理解 - 很弱时,我倾向于在之后编写测试。

答案 2 :(得分:6)

TDD不是关于测试,而是测试如何驱动您的代码。 所以基本上你是在编写测​​试来让架构自然地进化(并且不要忘记重构!!!否则你不会从中获得很多好处)。 你之后拥有一系列回归测试和可执行文档是一个很好的副作用,但不是TDD背后的主要原因。

所以我的投票是: 首先测试

PS:不,这并不意味着您之前不必计划您的架构,但如果测试告诉您这样做,您可能会重新考虑它!!!!

答案 3 :(得分:5)

过去6到7年,我一直领导开发团队。我可以肯定的是,作为一名开发人员和我曾经合作过的开发人员,如果我们知道代码在大局中的位置,它会对代码的质量产生显着的影响。

测试驱动开发(TDD)帮助我们回答“什么?”在我们回答“怎么样?”之前它会产生很大的不同。

我理解为什么可能会担心在PoC类型的开发/架构师工作中不遵循它。你是对的,它可能没有完全意义来遵循这个过程。同时,我想强调TDD是一个属于开发阶段的过程(我知道它听起来已经过时了,但是当低级规范明确时,你明白了。)

答案 4 :(得分:2)

我倾向于在编写代码时编写它们。在编写之前,我最多会编写类/模块是否存在的测试。

我在计划测试之前编写测试的详细程度还远远不够。

我不知道这是我的想法或方法中的缺陷还是仅仅是TIMTOWTDI。

答案 5 :(得分:2)

并非总是如此,但我发现它确实对我有帮助。

答案 6 :(得分:2)

我从如何调用我的“单元”开始编译。 像:

picker = Pick.new
item=picker.pick('a')
assert item

然后我创建

class Pick
 def pick(something)
 return nil
 end
end

然后我继续在我的“测试”案例中使用Pick,这样我就可以看到我希望如何调用它以及如何处理不同类型的行为。每当我意识到我可能在某些边界或某种错误/异常上遇到麻烦时,我会尝试解决它并获得一个新的测试用例。

所以,简而言之。是。 之前进行测试的比例远远高于不进行测试。

答案 7 :(得分:2)

我已经编程了20年了,我几乎从来没有写过一行代码,我没有进行某种单元测试 - 老实说,我知道人们一直这样做,但是有人怎么做可以发送一行没有进行过某种测试的代码就超出了我。

通常,如果没有测试框架,我只需在我编写的每个类中编写一个main()。它为你的应用添加了一些小小的东西,但是如果他们想要的话,有人可以随时删除它(或评论出来)。我真的希望你的类中只有一个test()方法可以自动编译出版本 - 我喜欢我的测试方法和我的代码在同一个文件中......

所以我已经完成了测试驱动开发和测试开发。我可以告诉你,当你是一名初学程序员时,TDD真的很有帮助。它可以帮助您学习查看代码“从外部”,这是程序员可以学习的最重要的课程之一。

TDD还可以帮助您在遇到困难时继续前进。你可以写一些你知道你的代码必须做的非常小的部分,然后运行并修复它 - 它会让人上瘾。

另一方面,当您添加到现有代码并且几乎完全了解您想要的内容时,这是一个折腾。您的“其他代码”通常会测试您的新代码。您仍然需要确保测试每条路径,但只需从前端运行测试就可以获得良好的覆盖范围(动态语言除外 - 对于那些无论如何都应该对所有内容进行单元测试的人)。 / p>

顺便说一下,当我在一个相当大的Ruby / Rails项目上时,我们的测试覆盖率非常高。我们将一个主要的中心模型类重构为两个类。这将花费我们两天时间,但是我们不得不重新进行所有测试,最终接近两周。测试不是完全免费的。

答案 8 :(得分:2)

我认为首先编写测试有助于定义代码应该实际执行的操作。很多时候人们没有很好地定义代码应该做什么或它应该如何工作。他们只是开始写作并随着时间的推移进行补充。首先创建测试使您专注于代码将执行的操作。

答案 9 :(得分:2)

指令建议您如何做一些事情来提高整体质量或生产力,甚至是最终产品。他们决不会遵守法律,而不会被正确的编码实践之神一闪而过。

这是我对拍摄的妥协,我发现它非常有用和富有成效。

通常最难实现的部分是要求,并且紧随其后的是您的类,API,包的可用性......然后是实际的实现。

  1. 编写你的界面(他们会改变,但在知道 必须完成的情况下会有很长的路要走)
  2. 编写一个简单的程序来使用接口(它们是愚蠢的主要)。这对于确定将要使用的 HOW 有很长的路要走(根据需要经常回到1)
  3. 在界面上编写测试(从TDD集成的位,再次根据需要返回1)
  4. 编写接口背后的实际代码
  5. 在类和实际实现上编写测试,使用覆盖工具确保不要忘记weid执行路径
  6. 所以,是的,我在编码之前编写了测试,但从未弄清楚需要在一定程度的细节上做些什么。这些通常是高级测试,只将整体视为黑盒子。通常将保持为集成测试,并且一旦接口稳定,就不会有太大变化。

    然后我在它背后的实现上写了一堆测试(单元测试),这些测试将更加详细,并且随着实现的发展而经常变化,因为它得到了优化和扩展。

    这严格来说是TDD吗?极端?敏捷...?随你... ?我不知道,坦白说我不在乎。它适用于。我根据需要调整它,随着我对软件开发实践的理解的发展。

    我的2美分

答案 10 :(得分:1)

现在有很多答案,它们都是不同的。这完全类似于那里的现实。每个人都采用不同的方式。我认为对单元测试存在巨大的误解。在我看来好像人们听说过TDD,他们说这很好。然后他们开始编写单元测试而没有真正理解TDD到底是什么。他们只是得到了“哦,是的,我们必须写测试”,他们同意这一点。他们也听说过“你应该先写下你的测试”,但他们并不认真对待。

我认为这是因为他们不了解测试优先的好处,反过来,只有在你这样做了一段时间后才能理解。他们似乎总是找到1.000.000的借口,为什么他们不喜欢先写测试。因为在弄清楚所有东西是如何组合在一起等等时太难了。在我看来,这是他们隐藏自己无法一次训练自己的所有借口,尝试先测试方法并开始看到好处。 / p>

如果他们开始争辩说“我不会对这个测试第一件事感到满意,但我从来没有这样做过”,这是最荒谬的事情......很棒......

我想知道单元测试最初来自哪里。因为如果这个概念真的来自TDD,那么人们如何弄错它就是荒谬的。

答案 11 :(得分:1)

我不确定,但是根据你的描述,我觉得可能会对测试优先实际意味着什么产生误解。 意味着您首先编写所有您的测试。它确实意味着你有一个非常紧凑的循环

  1. 写一个最小的测试
  2. 通过编写必要的最小生产代码来完成测试通过
  3. 编写下一个失败的测试
  4. 通过以最简单的方式更改现有生产代码来完成所有现有测试
  5. 重构代码(测试和生产!),以便它不包含重复并具有表现力
  6. 继续3.直到你想不出另一个合理的测试
  7. 一个周期(3-5)通常只需要几分钟。使用这种技术,您可以在并行编写测试和生产代码时实际进化设计。根本没有涉及前期设计。

    关于它是“必要”的问题 - 不,显然不是。在没有进行TDD的情况下,已经成功完成了无数项目。但有一些强有力的证据表明,使用TDD通常可以显着提高质量,通常不会对生产力产生负面影响。它也很有趣!

    哦,关于它感觉不“自然”,这只是你习惯的问题。我知道每隔几分钟就会沉迷于获得绿色条形码(典型的xUnit标志为“所有测试通过”)的人。

答案 12 :(得分:1)

之前,期间和之后。 以前是规范的一部分,合同,工作的定义 在特殊情况下,在执行时发现异常数据,异常。 之后是维护,演变,变革,新要求。

答案 13 :(得分:1)

就个人而言,我认为如果在编写代码之前没有完成,单元测试会失去很多有效性。

测试中一个古老的问题是,无论我们如何思考它,我们都不会想出任何可能的方案来编写测试来覆盖。

显然,单元测试本身并不能完全阻止这种情况,因为它是限制性测试,仅查看一个代码单元而不是覆盖此代码与其他所有代码之间的交互,但它为第一个编写干净代码提供了良好的基础应该至少限制模块之间交互问题的机会。我一直致力于保持代码尽可能简单的原则 - 事实上我相信这是TDD的关键原则之一。

所以从一个基本上说你可以创建这种类的类并构建它的测试开始,理论上,为每行代码编写一个测试或者至少覆盖通过特定代码段的每个路由。随你设计!显然,基于最初生成的粗略前端设计,为您提供一个工作框架。

正如你所说的那样,开始时非常不自然,看起来似乎是浪费时间,但我已经亲眼看到,从长远来看,当缺陷统计数据出现并显示完全模块时,它会得到回报使用TDD编写的缺陷随着时间的推移缺陷要少得多。

答案 14 :(得分:1)

首先编写测试定义了代码的外观 - 即它会使代码更加模块化和可测试,因此您不会创建具有非常复杂和重叠功能的“膨胀”方法。这也有助于在单独的方法中隔离所有核心功能,以便于测试。

答案 15 :(得分:0)

我是在同一时间写的。我为新类和测试类创建了框架代码,然后我为一些功能编写了一个测试(然后帮助我看看我希望如何调用新对象),并在代码中实现它。

通常情况下,我第一次没有优雅的代码,通常是非常hacky。但是一旦所有的测试都有效,你就可以重构,直到你得到一些非常整洁,可以证明是坚如磐石的东西。

答案 16 :(得分:0)

当你写一些你用来写作的东西时,它会帮助你先写下你经常检查的东西,然后再编写这些功能。更多时候,对于您正在编写的软件而言,这些功能并不是最重要的。现在,在另一边没有银子弹,不应该遵循这封信。开发人员的判断在决定使用测试驱动开发与后期测试开发中起着重要作用。

答案 17 :(得分:0)

是的,如果您使用的是真正的TDD原则。否则,只要你正在编写单元测试,你就会比大多数人做得更好。

根据我的经验,在代码之前编写测试通常更容易,因为通过这样做,您可以在编写代码时为自己提供一个简单的调试工具。

答案 18 :(得分:0)

请记住使用极限编程,您的测试有效地是您的文档。所以如果你不知道你在测试什么,那么你不知道你的应用程序将要做什么?

您可以从“故事”开始,这可能类似于

“用户可以获取问题列表”

然后当你开始编写代码来解决单元测试时。要解决上述问题,您至少需要一个用户和问题类。那么你就可以开始考虑这些领域了:

“用户类具有名称DOB地址TelNo锁定字段”

等。 希望它有所帮助。

狡猾

答案 19 :(得分:0)

我没有先写实际的单元测试,但是在开始编写列出所有可能需要测试的场景之前,我确实做了一个测试矩阵。我还列出了在对程序的任何部分进行更改时必须进行测试的情况列表,作为回归测试的一部分,除了完全测试应用程序的一些代码之外,还将涵盖应用程序中的大多数基本方案。改变。