TDD实践:区分真实失败和未实现功能

2 投票
4 回答
1174 浏览
提问于 2025-04-17 05:41

如果你正在进行测试驱动开发(TDD),你怎么知道哪些测试失败是因为现有代码真的有问题,哪些是因为测试本身有问题或者相关功能还没实现呢?请不要说“你根本不在乎,因为你都得修复。”我想超越这种思维方式。

我写测试的一般步骤如下:

  • 首先,我会设计测试套件的大致结构,可能是全部或部分。也就是说,我会先写下测试的名称,以提醒自己要实现的功能。通常(至少在Python中),我会让每个测试只有一行代码:self.fail()。这样,我可以顺着思路列出我想测试的每个功能,比如一次列出11个测试。

  • 第二,我选择一个测试,实际编写测试逻辑。

  • 第三,我运行测试工具,看到11个失败的测试——10个是简单的self.fail(),1个是真正的AssertionError。

  • 第四,我编写代码,让我的测试通过。

  • 第五,我再次运行测试工具,看到1个通过和10个失败。

  • 第六,我回到第二步。

理想情况下,我希望在测试中,不仅仅看到通过、失败和异常,还能有第四种可能性:未实现(NotImplemented)。

这里的最佳实践是什么呢?

4 个回答

1

大多数项目都有一个层级结构,比如项目->包->模块->类。如果你能选择性地运行某个层级的测试,或者你的报告详细覆盖了这些部分,你就能很清楚地看到它们的状态。通常情况下,当一个整个包或类测试失败时,往往是因为它还没有被实现。

另外,在很多测试框架中,你可以通过去掉某个测试方法的注释或装饰,或者重命名这个方法,来禁用单个测试用例。不过,这样做的缺点是你看不到实现的进展。不过,如果你给这些方法设定一个固定的前缀,可能可以很容易地从你的测试源代码中找到相关信息。

说到这里,我希望有一个测试框架能区分这些状态,并且除了常见的测试状态代码(比如通过、警告和失败)之外,还能有一个“未实现”的状态。我想可能有些框架是这样做的。

2

我会用一张纸来列一个测试清单(就像一个备忘录,帮助我跟踪测试,确保不漏掉任何一个)。我希望你不是一次性把所有失败的测试都写下来(因为这样会导致在每次红-绿-重构的过程中,随着新知识的加入,可能会有些混乱)。

如果你想把一个测试标记为待办或未实现,你可以用类似于 [Ignore("PENDING")][Ignore("TODO")] 的方式来标记这个测试。例如,NUnit会把这样的测试显示为黄色,而不是标记为失败。所以,红色表示测试失败,黄色表示待办。

4

有一些测试驱动开发(TDD)工具区分了“待处理测试”和“失败测试”。我记得 unittest2 也有这样的区分。

(我记得你可以这样写:

def test_this_thing(self):
  pass

... 不过这是我记得的内容...

[编辑:在 Python 2.7 的 unittest 或 unittest2 中,你可以用 @skip@unittest.expectedFailure 装饰器来标记测试。可以查看这方面的文档

撰写回答