如果修复涉及更改测试签名下的方法,如何使用TDD?

时间:2009-04-08 06:25:22

标签: unit-testing refactoring tdd methodology

我正试图了解TDD方法并且遇到了 - 我认为是 - 一个鸡蛋问题:如果错误修复涉及更改方法的签名该怎么办。

考虑以下方法签名:

string RemoveTokenFromString (string delimited, string token)

顾名思义,此方法从token中删除delimited的所有实例,并返回结果字符串。

我后来发现这个方法有一个错误(例如从字符串中删除了错误的位)。因此,我编写了一个测试用例,描述了发生错误的场景,并确保测试失败。

修复bug时,我发现该方法需要更多信息才能正常工作 - 这些信息只能作为参数发送(被测方法是静态类的一部分)

那我该怎么办?如果我修复了这个bug,这迫使我改变单元测试 - 这是'正确的'TDD方法吗?

7 个答案:

答案 0 :(得分:7)

你已陷入TDD中最危险的陷阱:你认为TDD是关于测试的,但事实并非如此。但是,由于TDD中的所有术语都与测试有关,因此很容易陷入该陷阱。这就是BDD被发明的原因:它本质上是TDD,但没有令人困惑的术语。

在TDD中,测试不是真正的测试,它们就是例子。断言并不是真正的断言,它们是期望。而且你不是在处理单位,而是处理行为。 BDD只是称他们为。 (注意:BDD自最初发明以来已经发展,现在它包含了不属于TDD的东西,但最初的意图只是“很多人做TDD错误,所以用不同的词来帮助他们做正确的事”。 )

无论如何,如果你认为测试不是一个测试,而是一个关于该方法应该如何工作的行为示例,那么显而易见的是,当您对预期行为有更好的理解时,删除或更改测试不是只有TDD允许,它才是唯一正确的选择!始终牢记这一点!

答案 1 :(得分:6)

当您发现设备的预期行为发生变化时,对您的测试进行轰炸绝对没有错。

//Up front
[Test]
public void should_remove_correct_token_from_string()
{
  var text = "do.it.correctly..";
  var expected = "doitcorrectly";
  Assert.AreEqual(StaticClass.RemoveTokenFromString(text, "."), expected);
}

//After finding that it doesn't do the right thing
//Delete the old test and *design* a new function that
//Does what you want through a new test
//Remember TDD is about design, not testing!
[Test]
public void should_remove_correct_token_from_string()
{
  var text = "do.it.correctly..";
  var expected = "doitcorrectly";
  Assert.AreEqual(
      StaticClass.RemoveTokenFromString(
          text,
          ".",
          System.Text.Encoding.UTF8), expected);
}

//This will force you to add a new parameter to your function
//Obviously now, there are edge cases to deal with your new parameter etc.
//So more test are required to further design your new function

答案 2 :(得分:4)

保持简单。

如果您的单元测试错误或已过时,则必须重写它。如果您的规格发生变化,或某些规格不再相关,则您的单元测试必须反映出来。

红色,绿色,重构适用于您的单元测试,而不仅仅是您正在测试的代码。

答案 3 :(得分:2)

有一个名为Add Parameter重构可以提供帮助。

如果您的语言支持method overloading,您可以先使用新参数创建新功能,复制现有功能的主体并解决问题。

然后,当问题得到解决时,您可以逐个修改所有测试以调用新方法。最后你可以删除旧方法。

使用不支持方法重载的语言,创建一个具有不同名称的新函数,在该新函数中复制现有函数的主体,让现有函数调用新函数,可能具有虚拟值新参数。那么你可以通过所有测试。让您的旧测试逐个调用新函数。当不再使用旧方法时,可以删除它并重命名新函数。

这有点过程广泛,但我认为这个遵循red-green-refactor的TDD方式。

如果可用,参数的默认值也可以提供帮助。

答案 4 :(得分:1)

红色,绿色,重构。

无论你做什么,你首先要进入一个状态,你有一个编译但失败的测试用例,可以重现bug。然后,您可以继续将参数添加到测试和实现中,但不执行任何操作,因此您仍然可以使用Red。

答案 5 :(得分:1)

我会说不要担心'正确'/'正确'的方式......无论如何帮助你更快地接近解决方案。

如果您发现需要接受额外的参数,

  • 更新测试用例中的呼叫
  • 将新参数添加到实际方法
  • 验证您的代码是否已构建且测试再次失败。
  • 继续将其设为绿色。

只有在添加新参数会导致数以万计的编译错误的情况下,我才会建议 - 按照小步骤进行操作......你不想更新整个源代码库,然后才发现你真的不需要第三个参数或者你需要第四个...时间丢失。因此,在更新所有引用之前,请使用该方法的新版本“正常工作”。 (正如菲利普所说)

  • 使用添加的参数
  • 编写新的重载
  • 将旧方法的代码移动到新的重载
  • 使用旧参数
  • 的某个默认值创建旧的过载中继或委托给新的重载
  • 现在你可以回到手头的任务,让新测试变为绿色。
  • 如果您不再需要旧的重载,请将其删除并修复生成的编译错误。

答案 6 :(得分:0)

如果方法没有正确地完成工作,则需要修复它,如果修复需要更改签名,那么注意到错误。根据TDD,您首先编写测试用例,这肯定会失败,然后您编写方法来满足测试。按照这种方法,如果测试中的方法调用需要一个参数来使其起作用,那么你需要这样做。