8

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

考虑以下方法签名:

string RemoveTokenFromString (string delimited, string token)

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

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

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

那我该怎么办?如果我修复了这个错误,这会迫使我改变单元测试——那会是“正确的”TDD 方法吗?

4

7 回答 7

7

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

在 TDD 中,测试并不是真正的测试,它们只是示例。断言并不是真正的断言,它们是期望。而且您不是在处理单位,而是在处理行为。BDD 只是这样称呼他们。(注意:BDD 自最初发明以来一直在发展,现在它包含了不属于 TDD 的内容,但最初的意图只是“很多人做错了 TDD,所以用不同的词来帮助他们做对”。 )

无论如何,如果您认为测试不是测试,而是该方法应该如何工作的行为示例,那么随着您对预期行为的更好理解,删除或更改测试不仅允许TDD,是唯一正确的选择!永远记住这一点!

于 2009-04-08T17:41:28.047 回答
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
于 2009-04-08T06:39:20.993 回答
4

把事情简单化。

如果你的单元测试是错误的或过时的,你必须重写它。如果您的规格发生变化,或者某些规格不再相关,您的单元测试必须反映这一点。

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

于 2009-04-08T06:45:49.637 回答
2

有一个称为Add Parameter的重构可以在这里提供帮助。

如果您的语言支持方法重载,您可以先使用新参数创建新函数,复制现有函数的主体并修复您的问题。

然后当问题解决后,您可以一一修改所有测试,以调用新方法。最后,您可以删除旧方法。

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

这有点过程广泛,但我认为这遵循红绿重构的 TDD 方式。

如果可用,参数的默认值也可能有所帮助。

于 2009-04-08T06:41:24.750 回答
1

红色,绿色,重构。

无论你做什么,你首先要达到一个状态,即你有一个正在编译但失败的测试用例重现了这个错误。然后,您可以继续将参数添加到测试和实现中,但不对其进行任何操作,因此您仍然拥有 Red。

于 2009-04-08T06:29:54.340 回答
1

我想说不要担心“正确”/“正确”的方式......任何可以帮助您更快地接近解决方案的方法。

如果你发现你需要接受一个额外的参数,

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

只有在添加新参数会导致无数编译错误的情况下,我才会推荐 - 采取小步骤......你不想在发现你真的不需要第三个参数之前更新整个源代码库或你需要第四个……时间浪费了。因此,在更新所有引用之前,让新版本的方法“工作”。(正如菲利普在这里所说)

  • 使用添加的参数编写新的重载
  • 将旧方法的代码移动到新的重载中
  • 使用新参数的一些默认值使旧的过载中继或委托给新的过载
  • 现在您可以回到手头的任务并让新测试变为绿色。
  • 如果您不再需要旧的重载,请将其删除并修复产生的编译错误。
于 2009-04-08T07:59:52.080 回答
0

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

于 2009-04-08T06:37:57.427 回答