单元测试应该如何记录?

发布于 2024-08-10 14:39:42 字数 1698 浏览 10 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

二智少女猫性小仙女 2024-08-17 14:39:42

我在单元测试中仅使用方法名称来记录大部分内容:

testInitializeSetsUpChessBoardCorrectly()
testSuccessfulPromotionAddsCorrectPiece()

对于几乎 100% 的测试用例,这清楚地解释了单元测试正在验证的内容,这就是我所使用的全部内容。然而,在一些更复杂的测试用例中,我将在整个方法中添加一些注释来解释几行的作用。

我以前见过一个工具(我相信它是针对 Ruby 的),它可以通过解析项目中所有测试用例的名称来生成文档文件,但我不记得这个名字了。如果您有国际象棋 Queen 类的测试用例:

testCanMoveStraightUpWhenNotBlocked()
testCanMoveStraightLeftWhenNotBlocked()

该工具将生成一个 HTML 文档,其内容如下:

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

I document most on my unit tests with the method name exclusively:

testInitializeSetsUpChessBoardCorrectly()
testSuccessfulPromotionAddsCorrectPiece()

For almost 100% of my test cases, this clearly explains what the unit test is validating and that's all I use. However, in a few of the more complicated test cases, I'll add a few comments throughout the method to explain what several lines are doing.

I've seen a tool before (I believe it was for Ruby) that would generate documentation files by parsing the names of all the test cases in a project, but I don't recall the name. If you had test cases for a chess Queen class:

testCanMoveStraightUpWhenNotBlocked()
testCanMoveStraightLeftWhenNotBlocked()

the tool would generate an HTML doc with contents something like this:

Queen requirements:
 - can move straight up when not blocked.
 - can move straight left when not blocked.
猥︴琐丶欲为 2024-08-17 14:39:42

也许问题不在于如何最好地编写测试文档字符串,而在于如何编写测试本身?以自我记录的方式重构测试可以大有帮助,并且当代码更改时,您的文档字符串不会变得过时。

您可以采取一些措施来使测试更加清晰:

  • clear &描述性测试方法名称(已经提到)
  • 测试主体应该清晰简洁(自我记录)
  • 抽象出方法中复杂的设置/拆卸等

例如,如果您有这样的测试:

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

请注意,您的测试方法现在由一系列带有意图显示名称的方法调用组成,这是一种特定于您的测试的 DSL 。这样的测试还需要文档吗?

另一件需要注意的事情是,您的测试方法主要处于一个抽象级别。阅读测试方法的人会看到算法是:

  • 创建一个小部件
  • ,在小部件上调用 run
  • 断言代码做了我们期望的事情

他们对测试方法的理解不会被设置小部件的细节所混淆,这是一个抽象级别低于测试方法。

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

一般来说,我反对评论,除非它们解释了代码的“原因”。阅读鲍勃·马丁叔叔的干净代码让我确信了这一点。有一个关于注释的章节,还有一个关于测试的章节。我推荐它。

有关自动化测试最佳实践的更多信息,请查看 xUnit 模式

Perhaps the issue isn't in how best to write test docstrings, but how to write the tests themselves? Refactoring tests in such a way that they're self documenting can go a long way, and your docstring won't go stale when the code changes.

There's a few things you can do to make the tests clearer:

  • clear & descriptive test method names (already mentioned)
  • test body should be clear and concise (self documenting)
  • abstract away complicated setup/teardown etc. in methods
  • more?

For example, if you have a test like this:

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

You might replace the setup statements with a method call:

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

Note that your test method is now composed of a series of method calls with intent-revealing names, a sort of DSL specific to your tests. Does a test like that still need documentation?

Another thing to note is that your test method is mainly at one level of abstraction. Someone reading the test method will see the algorithm is:

  • creating a widget
  • calling run on the widget
  • asserting the code did what we expect

Their understanding of the test method is not muddied by the details of setting up the widget, which is one level of abstraction lower than the test method.

The first version of the test method follows the Inline Setup pattern. The second version follows Creation Method and Delegated Setup patterns.

Generally I'm against comments, except where they explain the "why" of the code. Reading Uncle Bob Martin's Clean Code convinced me of this. There is a chapter on comments, and there is a chapter on testing. I recommend it.

For more on automated testing best practices, do check out xUnit Patterns.

可可 2024-08-17 14:39:42

测试方法的名称应该准确描述您正在测试的内容。文档应该说明导致测试失败的原因。

The name of the test method should describe exactly what you are testing. The documentation should say what makes the test fail.

想挽留 2024-08-17 14:39:42

您应该在文档字符串中使用描述性方法名称和注释的组合。一个好方法是在文档字符串中包含基本过程和验证步骤。然后,如果您从某种自动运行测试并收集结果的测试框架运行这些测试,您可以让框架记录每个测试方法的文档字符串的内容及其 stdout+stderr。

这是一个基本示例:

class SimpelTestCase(unittest.TestCase):
    def testSomething(self):
        """ Procedure:
            1. Print something
            2. Print something else
            ---------
            Verification:
            3. Verify no errors occurred
        """
        print "something"
        print "something else"

将过程与测试一起使用可以更轻松地弄清楚测试正在做什么。如果您将文档字符串包含在测试输出中,则可以更轻松地在以后查看结果时找出问题所在。我之前工作的地方就做过类似的事情,出现故障时效果很好。我们使用 CruiseControl 在每次签入时自动运行单元测试。

You should use a combination of descriptive method names and comments in the doc string. A good way to do it is including a basic procedure and verification steps in the doc string. Then if you run these tests from some kind of testing framework that automates running the tests and collecting results, you can have the framework log the contents of the doc string for each test method along with its stdout+stderr.

Here's a basic example:

class SimpelTestCase(unittest.TestCase):
    def testSomething(self):
        """ Procedure:
            1. Print something
            2. Print something else
            ---------
            Verification:
            3. Verify no errors occurred
        """
        print "something"
        print "something else"

Having the procedure with the test makes it much easier to figure out what the test is doing. And if you include the docstring with the test output it makes figuring out what went wrong when going through the results later much easier. The previous place I worked at did something like this and it worked out very well when failures occurred. We ran the unit tests on every checkin automatically, using CruiseControl.

惟欲睡 2024-08-17 14:39:42

当测试失败时(应该是在它通过之前),您应该看到错误消息并能够知道发生了什么。只有当你这样计划时才会发生这种情况。

这完全是测试类、测试方法和断言消息的命名问题。当测试失败,并且您无法从这三个线索判断出什么情况时,请重命名一些内容或分解一些测试类。

如果夹具的名称为 ClassXTests 并且测试的名称为 TestMethodX 并且错误消息为“预期 true,返回 false”,则不会发生这种情况。这是测试写作草率的标志。

大多数时候,您不必阅读测试或任何评论就可以知道发生了什么。

When the test fails (which should be before it ever passes) you should see the error message and be able to tell what's up. That only happens if you plan it that way.

It's entirely a matter of the naming of the test class, the test method, and the assert message. When a test fails, and you can't tell what is up from these three clues, then rename some things or break up some tests classes.

It doesn't happen if the name of the fixture is ClassXTests and the name of the test is TestMethodX and the error message is "expected true, returned false". That's a sign of sloppy test writing.

Most of the time you shouldn't have to read the test or any comments to know what has happened.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文