红绿灯测试的相关性

时间:2009-01-01 10:46:20

标签: unit-testing language-agnostic tdd

我正在开始,并且热爱TDD,无论如何对红绿灯概念感到疑惑。理论上我理解确保在通过测试之前可以通过测试的重要性。然而,在实践中,我发现这有点徒劳无功。

我觉得如果不实现我打算测试的代码,我就无法正确编写失败或通过测试。例如,如果我编写一个测试来显示我的DataProvider正在返回DataRow,我需要编写DAL逻辑以提供有意义的失败,一个失败超过NullException或从空方法返回Null,这似乎是毫无意义,因为我觉得红灯应该表明我可以根据我测试的实际逻辑创建一个失败的测试。

换句话说,如果我只是返回null或false,从我正在测试的函数中得到我的失败,真正的红灯值。

但是如果我已经实现了逻辑(这在某种程度上违背了测试的第一范式),我发现我只是为了测试互斥概念(IsTrue而不是IsFalse,或IsNull而不是IsNotNull)获得红灯而不是绿灯,然后将它们切换到相反的位置以获得通行证。

我没有理解这个概念,我真的在提出这个问题,因为这是我注意到的事情,我想知道我做错了什么。

修改

我接受了查理·马丁的答案,因为它对我来说效果最好,并没有暗示其他答案没有效力,所有这些都帮助我理解了一个我显然没有正确理解的概念

5 个答案:

答案 0 :(得分:9)

红色权利的价值在于它发现误报的能力。我发现无论我的实现代码是什么,它总是通过测试。正是在这种情况下,红灯/绿灯测试才有帮助。

我也发现我的一些测试根本没有运行,当我没有使用红灯时,我所看到的只是“建立成功”。如果我使用红灯确保我的测试失败,那么当我看到构建失败时,我会怀疑构建是否成功。

答案 1 :(得分:3)

将其视为一种规范。你开始问自己,“我最终想要的代码应该怎样做?”所以,假设您想编写一个添加自然数的函数。你怎么知道这是否有效?好吧,你知道2 + 2 = 4,所以你可以写一个测试(这基本上是python,但遗漏了很多细节,见unittest module docs):

def test2plus2(self):
    assertEquals(addNat(2,2), 4)

所以在这里你已经定义了一个规范,上面写着“对于自然数a和b,计算a + b”。 现在您知道编写函数所需的内容

def addNat(a,b):
    return a+b

你运行它并通过测试。但是,你知道还有其他一些事情;因为它只是为了自然数(无论出于何种原因),你需要添加一个防止非自然数字的守卫:

def testUnnatural(self):
    failUnlessRaises(AssertionErrof, addNat(-1, 2))

现在你已经添加了一个说明“并且如果数字为负则抛出AssertionError”。它告诉你下一段代码:

def addNat(a,b):
    """Version two"""
    assert((a >= 0) and (b>=0))
    return a+b

现在运行,断言不会触发;再次成功。

关键是TDD是一种定义非常清晰的详细规范的方法。对于像“addNat”这样的东西,它们不是必需的,但是真正的代码,特别是在敏捷世界中,你直观地知道答案。 TDD可以帮助您对实际需求进行排序和分析,

答案 2 :(得分:1)

有几个激励性的例子我可以想到为什么红灯是有用的并且对我有很大的帮助。

  1. 为了理智而写一个红色的测试。我确信该测试可以验证我所知道的某些功能尚未实现,但真的真的不是。

  2. 当您在代码中发现错误时,您会编写一个指出此错误的失败测试。从一开始就进行了红色测试,你很确定你已经知道了,并且知道错误修复的时间。

  3. 可能曾经有一个例子,其中红灯没有用,而且当你正在编写测试以填充有效的功能时,它们通常从开始就是绿色。我会警告你写关于绿色测试的事情,可能会发生你必须重新设计课程和其他方面的问题,这使得一些测试过时了。 所有绿色测试写作都是徒劳的!

答案 3 :(得分:1)

我不确定我是否明白了这一点,但我知道这是怎么回事。

少考虑函数返回的内容,更多地考虑它的功能和假设是什么。

如果我的true / false函数是以下C函数的某种语言版本:

bool isIntPrime( int testInt )

然后你想确保测试失败,如果你传递一个double(而不是像某些语言中遇到的'有用的'隐式转换)。

如果你真的找不到“红灯”的情况,那么你的“绿灯”基本没有意义。如果你真的遇到这种情况,那么测试可能不会太值得,所以测试这个功能/功能有点浪费时间。也许它是如此简单和强大,有效,不能失败?然后写一堆“绿灯”测试是浪费你的时间。

有点像白兔思想实验。 如果我认为所有的兔子都是棕色的,那么计算棕色的兔子就无法确定我的主张的真实性。 然而,我看到的第一只白兔证明我的说法是假的。

这个简单的例子有帮助吗?

答案 4 :(得分:1)

我总是从我的代码开始抛出NotImplementedException,尽管有些人会声称你应该从没有实现方法开始,并且编译失败是你第一次失败的测试。这有一些逻辑,如果您可以在不使用该方法的情况下编写测试(并且它通过),那么您不需要编写任何代码。不过,我通常会这样做。

编写了抛出异常的代码之后,我继续为我正在处理的功能编写第一个测试并获得第一个红灯(大概)。现在我能够继续TDD的常规节奏 - Red-Green-Refactor。不要忘记最后一步。在测试通过时重构 - 而不是在编写代码以纠正失败的测试时。

这种方法需要遵守纪律,有时看起来你做的很蠢,因为通常第一次测试通过最简单的方法就是返回一些硬编码数据。但是,坚持不懈;这个引导阶段相对较短,如果你不跳过它,你可能会发现你编写的代码更简单,更易于维护,因为你的解决方案(或者至少是骨架)在第一次测试时神奇地变成了整体。如果你没有以小增量进行开发,那么你就没有进行TDD。

强制性免责声明:不要忘记TDD是关于单元测试的。还有其他类型的测试(集成,接受,加载......),当你开始做TDD时,对其他类型测试的需求并没有神奇地消失。