单元测试应如何记录?

29 投票
5 回答
21580 浏览
提问于 2025-04-15 15:57

我正在努力提高我Python项目中测试的数量和质量。随着测试数量的增加,我遇到的一个困难是搞清楚每个测试的作用,以及它们是如何帮助我发现问题的。我知道,跟踪测试的一部分是给单元测试起个好名字(这个问题在其他地方已经讨论过了),但我也想了解文档和单元测试是如何结合在一起的。

单元测试应该如何记录,以便在将来这些测试失败时能更好地帮助我们?具体来说,什么样的单元测试文档字符串才算好?

我希望能得到一些详细的回答和优秀文档的单元测试示例。虽然我只用Python,但我也愿意学习其他语言的做法。

5 个回答

4

测试方法的名字应该清楚地说明你在测试什么。文档里应该写明是什么原因导致测试失败。

15

也许问题不在于如何写测试文档,而在于如何编写测试本身?如果能把测试重构得更自我说明,那就能大大提高测试的可读性,而且当代码发生变化时,你的文档也不会过时。

为了让测试更清晰,你可以做几件事:

  • 使用清晰且描述性的测试方法名称(之前提到过)
  • 测试的主体应该简洁明了(自我说明)
  • 把复杂的准备和清理工作放到单独的方法中
  • 还有其他吗?

举个例子,如果你有这样的测试:

def test_widget_run_returns_0():
    widget = Widget(param1, param2, "another param")
    widget.set_option(true)
    widget.set_temp_dir("/tmp/widget_tmp")
    widget.destination_ip = "10.10.10.99"

    return_value = widget.run()

    assert return_value == 0
    assert widget.response == "My expected response"
    assert widget.errors == None

你可以用一个方法调用来替换准备的语句:

def test_widget_run_returns_0():
    widget = create_basic_widget()
    return_value = widget.run()
    assert return_value == 0
    assert_basic_widget(widget)

def create_basic_widget():
    widget = Widget(param1, param2, "another param")
    widget.set_option(true)
    widget.set_temp_dir("/tmp/widget_tmp")
    widget.destination_ip = "10.10.10.99"
    return widget

def assert_basic_widget():
    assert widget.response == "My expected response"
    assert widget.errors == None

注意,现在你的测试方法是由一系列意图明确的方法调用组成,这就像是为你的测试定制的语言。这样的测试还需要文档吗?

另一个要注意的点是,你的测试方法主要处于一个抽象层次。阅读测试方法的人会看到算法是:

  • 创建一个小部件
  • 在小部件上调用运行
  • 确认代码的表现符合我们的预期

他们对测试方法的理解不会被设置小部件的细节所混淆,这些细节处于比测试方法更低的抽象层次。

测试方法的第一个版本遵循了内联设置模式。第二个版本遵循了创建方法委托设置模式。

一般来说,我不太赞成使用注释,除非它们能解释代码的“为什么”。阅读了Uncle Bob Martin的《整洁代码》让我相信了这一点。书中有一章专门讲注释,还有一章讲测试。我推荐这本书。

想了解更多自动化测试的最佳实践,可以查看xUnit Patterns

17

我在单元测试中大部分时间只用方法名来做文档说明:

testInitializeSetsUpChessBoardCorrectly()
testSuccessfulPromotionAddsCorrectPiece()

对于几乎100%的测试案例,这样做能清楚地说明这个单元测试在验证什么,我就只用这个方法。不过在一些比较复杂的测试案例中,我会在方法中添加一些注释,来解释几行代码的作用。

我之前见过一个工具(我记得是针对Ruby的),它可以通过解析项目中所有测试案例的名称来生成文档文件,但我不记得它的名字了。如果你有一个棋类游戏中的皇后类的测试案例:

testCanMoveStraightUpWhenNotBlocked()
testCanMoveStraightLeftWhenNotBlocked()

这个工具会生成一个HTML文档,内容大概是这样的:

Queen requirements:
 - can move straight up when not blocked.
 - can move straight left when not blocked.

撰写回答