2010-01-21 65 views
1

如果我编写测试代码来测试我的代码,测试代码可能有错误,所以需要测试,以及代码更改时我的测试代码可能需要更改。无限重复。如何解决TDD中的无限回归问题?

这个问题是如何解决的(在实践上和理论上)?

回答

5

测试测试代码,代码测试测试。

当你编写一个测试,然后编写足够的代码来运行它,测试失败。然后你编写代码使其通过。如果不这样做 - 如果它在之前通过,则编写代码,或者如果它失败后,出现问题。如果在编写代码之前通过测试,那么显然测试有问题 - 修复它,直到您遇到红色并且得到您预期的失败。如果测试是红色的并且在编写代码后不会变绿,那么有两件事情是错误的:测试或代码。找出它,修复它,然后继续前进。

+1

1以类似的静脉,斯图哈洛韦绘制一个平行于双会计分类账。 – 2010-01-21 23:47:31

+0

有趣。当代码改变时,测试也会改变,对吗?所以这增加了一倍的工作量,但没有无限的倒退,对吗? – user128807 2010-01-22 00:36:43

+0

@Michael你有链接到Stu Halloway双重账簿的东西吗? – user128807 2010-01-22 00:38:26

0

写出真正好的测试代码。完整记录下来,在写作时仔细考虑,并且在方法上要有条不紊。此外,如果您尽可能缩短个别测试时间,则可以增加代码覆盖率,同时尽可能减少创建错误(并且可见)的机会。

+0

@Stefan。如果这工作,那么你可以将这个想法应用到原始代码。如果原始代码可能有缺陷并需要测试,那么测试代码肯定会有错误。 – user128807 2010-01-21 23:21:32

2

实际上,通过确保测试在通过之前失败。 TDD不会奇迹般地消除代码中存在错误的所有能力,它有望减少错误数量,但作为设计技术它更重要。

它真正提高错误计数的地方是在重构时。当我重构并破坏了一个由测试建立的旧行为时,这些测试已经多次拯救了我的培根。

当通过之前测试失败,你可以放心,你实际上是实现代码的行为的方式,使测试通过,使得测试一个有效的。如果您更改了打破测试的代码,那么您需要考虑哪一个是正确的,并相应地进行调整。

当我读了你的问题,我看到一个潜在的期望,TDD将阻止所有的错误。它不会。但它会阻止一些。更重要的是,它可以防止重构时出现的错误,让您随着时间的推移改进设计,而不用担心退步。

但TDD真正闪耀的地方在于驾驶设计。它可以确保设计正确地考虑依赖关系,它是模块化的,并且它可以做你期望的事情(而不是做正确的事情 - 这必须是集成或验收测试的一部分)。

编辑:(在响应评论)我明白你的问题,并试图回答这个问题。由于我没有那么成功,我会再试一次。

如果测试是第一次看到失败再通,基本概念,它具有被处理,它无法测试任何错误(代码验证试验和测试用于测试的代码)。这显然取决于生产代码的通过,所以它测试了一些东西。更重要的是,它不能真正做到。还有更多层面的测试(整合,接受,也许是一般性质量保证),可以解决更深层次的问题。

但我的首要的一点是要挑战我的理解是这个问题的前提是:怎样才能TDD提供100%无bug代码,如果测试本身可能有错误。我对这个前提的回答是它不能。它做了其他事情(驱动器设计)。我希望澄清事情。

+0

@义海。我认为你错过了这个问题的关键。我的观点是,测试代码本身可能存在缺陷,因此需要进行测试。因此,存在无限回归。 – user128807 2010-01-21 23:20:53

0

单元测试并不意味着100%的防范bug。这只是一种提高代码质量的技术。从未实践过TDD的人会认为,如果增加了一个旨在通过测试的DOH,代码的整体可靠性不会提高!

解决单元测试的测试问题。这正是您为什么首先使测试失败的原因 - 这是为了提高测试的质量。这里再也没有绝对,但这种做法确实有帮助。像他们说的那样,测试测试代码,代码测试测试。这总比没有测试好。

只要回去测试并修理它们,当它们破裂时,那么,就是意味着发生。如果测试结果良好(即粒度细小)并测试一个非常狭窄的用例,他们会提醒您实际代码将会中断。

单元测试作为一种回归工具是无价的。宏观回归(代码编写后的几天,几周,几年)和微观回归(编写代码时)。顺便说一下,我完全发明了这些术语。如果他们是Martin Fowler或Bob叔叔所使用的真正术语,那么这就意味着我和他们一样精彩:)只是在开玩笑。

如此长时间的回归已经被很好地理解了,你在编写代码几个月后就改变了代码,并且测试会提醒你什么是坏的。另一方面,微观回归是编写代码并慢慢添加功能的时候。如果您没有测试,那么您可能会从代码将要使用的位置开始执行测试,只需修改该代码即可完成各种场景。后面的代码会破坏较早的使用案例的风险很高。

使用TDD,您所实现功能的所有用例(测试)都会保留。这意味着你几乎可以保证,你以后添加的任何内容都不会破​​坏更强大的代码。

0

尽可能简单地编写测试。

然后,您将有一个良性循环,您的代码变得尽可能简单以测试测试。

大概的理论就是这样。

0

这听起来像你正在考虑编写单元测试证明是正确的。这是2件独立的事情。测试代码提供了一组已知的输入并验证了输出 - 它们应该非常简单。设置输入,执行,验证输出。测试只检查是否为您在测试中实施的场景生成了预期输出。如果你编写的测试过于复杂以至于他们自己需要测试,那么你需要大大简化你的测试。你不应该为你的测试代码编写测试代码。

1

这个想法是,单元测试没有复杂的逻辑......每个基本上都应该包含一个动作和一个关于动作结果或行为的断言或断言。没有太多的循环或分支逻辑......没有什么可能会有任何错误不是很明显,你不会错过它。所以,正如其他人所说的那样,你只是不要为测试编写测试。

1

我最近在工作中与某人讨论过这个问题。提出这个问题的人有一个公平的观点,但最终,单元测试是一种实用且经过验证的技术。 TDD增加了进一步的“测试优先”方面,进一步降低了测试不正确的可能性,因为在编写任何产品代码之前它应该会失败。虽然在我看来,测试是错误的,但这个问题更多的是哲学上的争论,而不是实际的障碍。

,你需要测试代码来测试测试的参数仅持有任何重量我,当有问题的代码,形成一个可重复使用的框架或实用类的测试,其他单元测试夹具将使用。

示例: 在NUnit 2.5之前,我们有可重用的数据驱动测试类。它被许多其他测试所使用并依赖于它,而且它相当复杂。肯定很复杂,有bug。所以我们单元测试了测试类。 ..然后,当NUnit 2.5出来时,我们将所有测试交换使用并丢弃了我们自己的实现。

在另一方面,写得很好的测试代码很简单,不包含特殊的逻辑/分支,并有助于提高围绕生产代码脚手架。在可能的情况下,我们应该始终在其他经过充分测试的框架中利用这些功能,而不是在我们的测试课中进行任何逻辑。如果常规的单元测试代码变得足够复杂以致于有人认为它可能需要测试,我会认为这种方法可能有缺陷。