2009-01-01 139 views
1

我开始并热爱TDD,但是对红绿灯概念感到疑惑。理论上我理解确保在通过测试之前可以通过测试失败的重要性。然而,在实践中,我发现这种做法有些徒劳无功。红色绿灯测试的相关性

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

换句话说,如果我只是返回null或false,我正在测试一个函数来测试我的失败是什么,真正的红色光的价值。但是,如果我已经实现了逻辑(这在某种程度上违背了测试的第一范式),我发现我仅仅是为了测试互斥概念(IsTrue代替IsFalse,或者IsNull代替IsNotNull)获得红灯而不是绿灯,然后将它们切换到相反以获得通行证。

我没有去理解这个概念,我真的提出这个问题,因为它是我注意到的,并且想知道如果我做错了什么。

编辑

我接受查理·马丁的答案,因为它为我工作最好的,这是不以任何方式暗示,有在其他的答案没有效力,所有这些都让我明白一个概念,我是显然没有适当地养护

回答

3

认为它是一种规范。你自问开始“我最终希望能够做的代码应该怎么做?”所以,假设你想写一个添加自然数的函数。你怎么知道这是否有效?嗯,你知道2 + 2 = 4,所以你可以写一个测试(这基本上是蟒蛇,但留下了很多细节,请参见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可以帮助您分拣并处理真实的需求,

9

红色右边的价值在于它能够发现假阳性。它发生在我身上,无论我的实现代码是什么,它总是通过测试。正是在这种情况下,红灯/绿灯测试有所帮助。

在我身上也发生了一些我的测试根本没有运行,而当我没有使用红灯时,我所看到的只是“Build Succeeded”。如果我使用红灯确保我的测试失败,那么当我预计构建失败时,我会看到构建成功的那一分钟。

+1

准确无误。特别是对于更大的测试套件,它往往会发生的事情往往是你忘记添加测试用例。红色灯光显示出noop impl确保您的测试实际上正在运行。 – Ole 2009-01-01 15:52:02

1

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

  1. 为了理智,写一个红色测试。我确信测试可以验证我所知道的某些功能尚未实现,但真的很明显并非如此。

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

可能有一次红灯没有用的例子,那就是当你正在编写测试来填充可用的功能时,它们通常从开始就是绿色的。尽管我会提醒你编写绿色测试,但可能发生的情况是你必须重新设计类,而不是重复性,这使得一些测试过时。 所有的绿色测试写作工作没有任何!

+0

您可以随时编写一个测试,在添加之前预期额外的功能。即使它是一个简单的编译失败,至少也是一盏红灯。 – tvanfosson 2009-01-01 13:40:31

1

我不知道我是否明白自己的观点,但以下是我如何看待此事。

不用考虑函数返回什么,更多关于它的作用和它假定的是真实的。

如果我真/假功能是下面的C函数的一些语言版本:

bool isIntPrime(int testInt) 

那么你要确保一个测试,如果你传递一个双(而不是有一个“有用的”隐性故障就像你在某些语言中可能会遇到的那样)。

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

它有点像白兔想法实验。 如果我假定所有的兔子都是棕色的,那么计算棕色兔子对于确立我的主张是否准确无关。 但是,我看到的第一只白兔证明了我的说法是错误的。

这个微不足道的例子有帮助吗?

1

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

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

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

强制免责声明:不要忘记TDD是关于单元测试。还有其他类型的测试(集成,验收,加载......),当您开始进行TDD时,其他类型测试的需求不会奇迹般地消失。