TDD 与单元测试

我的公司在单元测试我们的代码方面是相当新的。一段时间以来,我一直在阅读 TDD 和单元测试,并确信它们的价值。我试图说服我们的团队,TDD 值得我们努力学习和改变我们编程的思维方式,但这是一场斗争。这就引出了我的问题。

TDD 社区中有很多人对编写测试和代码非常虔诚(我和他们在一起) ,但是对于一个正在与 TDD 斗争的团队来说,妥协还能带来额外的好处吗?

I can probably succeed in getting the team to write unit tests once the code is written (perhaps as a requirement for checking in code) and my assumption is that there is still value in writing those unit tests.

将陷入困境的团队引入 TDD 的最佳方式是什么?如果失败了,即使在编写代码之后还是值得编写单元测试吗?

剪辑

What I've taken away from this is that it is important for us to start unit testing, somewhere in the coding process. For those in the team who pickup the concept, start to move more towards TDD and testing first. Thanks for everyone's input.

跟进

我们最近开始了一个新的小项目,团队中的一小部分使用 TDD,其余的在代码之后编写单元测试。在我们完成了项目的编码部分之后,那些在编写代码之后编写单元测试的人惊讶地发现 TDD 编码人员已经完成了工作,并且使用了更加可靠的代码。这是一个赢得怀疑论者的好办法。我们还有很多成长的烦恼等着我们,但是意志之战似乎已经结束了。感谢所有提供建议的人!

9665 次浏览

在我看来,50% 的测试覆盖率与“代码优先,测试后”和100% 完成的库相比,要好于100% 的测试覆盖率和50% 完成的 TDD 库。一段时间之后,您的开发人员同事将有希望发现为他们编写的所有 public代码编写测试是一件有趣和有教育意义的事情,因此 TDD 将悄悄地进入他们的开发例程。

在编写代码之后编写单元测试是绝对值得的。只是有时候会比较困难,因为你的代码并没有被设计成可测试的,你可能把它复杂化了。

我认为将团队引入 TDD 的一个很好的实用方法是在过渡期或者可能是长期提供“在开发过程中进行测试”的替代方法。应该鼓励他们使用 TDD 代码段,这些代码段对他们来说是自然的。然而,在代码的某些部分似乎很难实现测试优先,或者当使用非敏捷的 A & D 过程预先确定的对象时,开发人员可以选择编写一小部分代码,然后编写覆盖该代码的测试,并重复这个过程。在编写代码之后立即为某些代码编写单元测试总比根本不编写任何单元测试要好。

如果团队在实现 TDD 方面举步维艰,但是他们之前没有创建任何单元测试... ... 那么在编写代码之后创建单元测试来开始他们的工作。即使是在代码之后编写的单元测试也比根本没有单元测试要好!

一旦他们精通了单元测试(以及随之而来的一切) ,那么您就可以让他们先创建测试... 然后再编写代码。

我刚在日历上看到这句话: “每一条规则,执行到极致,都会变得荒谬甚至危险。”所以我的建议是不要对此抱有宗教信仰。团队中的每个成员都必须在他们认为“正确”的测试之间找到一个平衡点。这样,你的团队中的每个成员都会变得更有效率(而不是,比如说,思考“为什么我要写这个 sti * * * * test? ?”).

So some tests are better than none, tests after the code are better than few tests and testing before the code is better than after. But each step has its own merits and you shouldn't frown upon even small steps.

好吧,如果你不先写测试,它就不是“测试驱动”,它只是测试。它本身就有好处,如果您已经有了添加测试的代码库,那么即使它不是 TDD 而只是测试,它也肯定是有用的。

首先编写测试是关注代码在编写之前应该做什么。是的,你也得到了一个测试,这样做是好的,但有些人可能会争辩说,这甚至不是最重要的一点。

我要做的是使用 TDD 在 toy projects like these(参见 Coding Dojo,Katas)上培训团队(如果你能让有经验的 TDD 程序员参加这样的研讨会,那就更好了)。当他们看到好处时,他们会将 TDD 用于真正的项目。但与此同时,不要强迫他们,如果他们看不到好处,他们不会这样做的权利。

如果他们是新的测试比 IMO 开始测试代码,已经写,慢慢地毕业编写测试第一。作为一个尝试学习 TDD 和新的单元测试的人,我发现很难做到180度大转弯,改变我的思维模式,在编写代码之前编写测试,所以我采取的方法是一种50-50的混合; 当我确切知道代码将是什么样子时,我将编写代码,然后编写一个测试来验证它。对于那些我不能完全确定的情况,我会从一个测试开始,然后一路向后。

还要记住,编写用于验证代码的测试,而不是编写用于满足测试的代码,这并没有什么错。如果你的团队不想走 TDD 路线,那么不要强迫他们。

TDD 是关于设计的!因此,如果您使用它,您将确保您的代码具有可测试的设计,从而使编写测试更加容易。如果你在编写代码之后编写测试,它们是 还是有价值的,但恕我直言,你将浪费时间,因为你可能没有一个可测试的设计。

为了说服您的团队采用 TDD,我可以给您一个建议,那就是使用 无畏的变革: 引入新思想的模式,作者: Mary Lynn Manns 和 Linda Rise中描述的一些技术。

Starting to build JUnit test classes is the way to start, for existing code it's the only way to start. In my experience it is very usefull to create test classes for existing code. If management thinks this will invest too much time, you can propose to only write test classes when the corresponding class is found to contain a bug, or is in need of cleanup.

For the maintenance process the approach to get the team over the line would be to write JUnit tests to reproduce bugs before you fix them, i.e.

  • 报告有窃听器
  • 如果需要,创建 JUnit 测试类
  • add a test that reproduces the bug
  • 修正你的代码
  • 运行测试以显示当前代码没有重现错误

您可以通过这种方式来“记录”bug,从而防止这些 bug 在以后再次出现。这是一个团队可以立即体验到的好处。

One of the most useful aspect of unit testing is ensuring the continuing correctness of already working code. When you can refactor at will, let an IDE remind you of compile time errors, and then click a button to let your tests spot any potential runtime errors--sometimes arriving in previously trivial blocks of code, then I think you will find your team starting to appreciate TDD. So starting with testing existing code is definitely useful.

而且,坦率地说,比起从 TDD 开始,通过尝试测试编写的代码,我学到了更多关于如何编写可测试代码的知识。如果您试图考虑既能实现最终目标又能允许测试的契约,那么一开始可能会过于抽象。但是当你看到代码时,你可能会说“这个单例模式完全破坏了依赖注入,使得测试变得不可能”,你就会开始理解什么样的模式让你的测试变得更容易。

这是你的团队在开始相信它之前必须有自己的成功的东西。我会向任何关心我的人大声抱怨我对 nUnit 的顿悟:

大约5年前,我在一个项目中发现了 nUnit。我们几乎已经完成了 V1.0,我创建了一些测试来试验这个新工具。我们有很多错误(很明显!)因为我们是一个新的团队,期限很紧,期望很高(听起来很熟悉?)等等。无论如何,我们得到了1.0,并开始在1.1。我们重组了一下团队,分配了两个开发人员给我。我为他们做了一个1小时的演示,并告诉他们,我们写的所有东西都必须有一个测试用例。在1.1开发周期中,我们不断地“落后”团队的其他成员,因为我们编写了更多的代码,即单元测试。我们最终做了更多的工作,但这是回报——当我们最终进入测试阶段时,我们的代码中正好有0个 bug。我们帮助其他人调试和修复他们的错误。在尸检中,当虫子数量出现时,引起了所有人的注意。

我不会傻到认为您可以通过测试来获得成功,但是当涉及到单元测试时,我是一个真正的信徒。这个项目采用了 nUnit,很快就扩展到了公司所有人。净项目由于1个成功。我们的 V1.1版本的总时间为9开发周,所以它肯定不是一夜之间的成功。但是长期来看,它证明了我们的项目和我们为之提供解决方案的公司是成功的。

毫无疑问,测试(先测试,然后测试,甚至之后测试)可以挽救你的生活,提高你的工作效率和信心。我建议你收养它!

我也处在类似的情况下,因为我是一个“菜鸟”开发人员,当我在团队项目中工作时,我经常会因为一个贡献破坏了构建而感到沮丧。我不知道我是否应该受到责备,甚至在某些情况下,我也不知道该责备谁。但我更担心的是,我正在对我的同事开发人员做同样的事情。这种认识促使我们采用一些 TDD 策略。我们的团队开始有愚蠢的游戏和规则,比如你不能回家,直到你所有的测试通过,或者如果你提交的东西没有测试,那么你必须给每个人买“啤酒/午餐/等”,这使得 TDD 更有趣。

TDD 是开发人员可以用来生成更好代码的工具。我碰巧觉得编写可测试代码的练习与测试本身一样没有价值。为了测试目的而隔离 IUT (测试下的实现)具有将代码解耦的副作用。

TDD 并不适合所有人,也没有什么魔法可以让团队选择去做。风险在于,不知道什么值得测试的单元测试编写者将编写大量低价值的测试,这将成为组织中 TDD 怀疑论者的炮灰。

我通常使自动化 验收测试不可协商,但允许开发人员采用 TDD,因为它适合他们。我让经验丰富的 TDDers 培训/指导其他人,并在几个月的时间里通过实例“证明”其有用性。

这既是一种技术上的变革,也是一种社会/文化上的变革。

我在许多组织中都做过这样的工作,我发现启动 TDD 的最佳方法就是建立结对编程。如果你可以指望其他人了解 TDD,那么你们两个可以分开,与其他开发人员配对,实际上使用 TDD 进行一些配对编程。如果没有,我会训练一个人谁将帮助你做到这一点,然后再把它提交给团队的其他人。

单元测试,尤其是 TDD 的主要障碍之一是开发人员不知道如何进行单元测试,因此他们不知道如何才能使其值得花费时间。而且,当你刚开始的时候,速度要慢得多,而且似乎也不会带来什么好处。它只有在你擅长的时候才能真正给你带来好处。通过设置成对的编程会话,您可以快速地让开发人员能够快速地学习它,并更快地熟练掌握它。此外,他们将能够看到从它的直接好处,因为你们一起工作通过它。

这种方法在过去曾多次对我奏效。

一旦编写了代码(可能作为检入代码的需求) ,我可以成功地让团队编写单元测试,我的假设是,编写这些单元测试仍然有价值。

毫无疑问,单元测试代码是有价值的(不管测试是什么时候编写的) ,我在“完成的定义”中包括“代码是单元测试的”。人们可以使用 TDD 或不使用 TDD,只要他们进行测试。

Regarding version control, I like to use "发展部门" with a 单元测试 policy (i.e. the code compiles and builds, all unit tests pass). When features are done, they are published from development branches to the trunk. In other words, the trunk branch is the "完成分公司" (No junk on the trunk!) and has a 可以装船 policy (can release at any time) which is more strict and includes more things than "unit tested".

发现 TDD 好处的一个强大方法是对一些现有功能进行重大重写,这可能是出于性能方面的原因。通过创建一套测试,这套测试可以很好地覆盖现有代码的所有功能,然后让您有信心重构内容,并且完全相信您的更改是安全的。

请注意,在本例中,我讨论的是测试设计或合同单元测试,这些测试实现细节在这里不适合。但话说回来,TDD 不能根据定义测试实现,因为它们应该在实现之前编写。

如果您在编写代码之前有设计会话,或者必须产生设计文档,那么您可以添加单元测试作为会话的有形结果。

然后,这可以作为代码应该如何工作的规范。在设计会议上鼓励结对,让人们讨论一些东西应该如何工作,以及在给定的场景中应该做什么。什么是边缘用例,它们有明确的测试用例,所以每个人都知道如果给定一个空参数会做什么。

除此之外,BDD也可能是令人感兴趣的

通过展示一两个例子,您可能会发现 TDD 导致编写的代码更少——因为您只编写通过测试所需的代码,所以更容易抵制镀金或参与 YAGNI 的诱惑。您不需要编写的代码不需要维护、重构等等,所以这是一个“真正的节省”,可以帮助推销 TDD 的概念。

如果你能清楚地展示出节省的时间、成本、代码和缺陷等方面的价值,你可能会发现它更容易推销。