编写作为另一个测试的子集的单元测试是否有任何价值?


15

举一个稍微做作的例子,假设我要测试一个函数返回两个数字,并且第一个小于第二个:

def test_length():
    result = my_function()
    assert len(result) == 2

def test_order()
    a, b = my_function()
    assert a < b

在这里,如果test_length失败,那么test_order也会失败。编写test_length或跳过是一种最佳实践吗?

编辑:请注意,在这种情况下,两个测试大多彼此独立,每个测试可以独立运行,也可以以相反的顺序运行,这无关紧要。所以这些以前的问题都没有

与上述内容重复。



2
@ GlenH7-这似乎是一个不同的问题。另一个是“如果具有A调用B并返回相同结果的函数,则应同时测试AB”。这更多是关于测试是重叠的,而不是测试中的功能。(尽管由于它们当前被命名而令人困惑)。
Telastyn


1
严格来说,这些测试并不是真正的重叠(它们不具有成功依赖性)。一个函数lambda: type('', (), {'__len__': lambda self: 2})()将传递第一个,但不会传递第二个。
liori 2015年

1
@ GlenH7:FWIW,我并没有真正改变这个问题,只是试图让它保持安全;-)(@gnat:没有冒犯性,只是在开玩笑!)
Doc Brown

Answers:


28

可能有价值,但这有点难闻。您的测试没有很好地隔离(因为test_order实际上测试了两件事),或者您的测试过于教条(使两个测试测试相同的逻辑东西)。

在示例中,我将两个测试合并在一起。是的,这意味着您有多个断言。太糟糕了。您仍在测试一件事-函数的结果。在现实世界中,有时这意味着要进行两次检查。


尽管错过了一个小问题,但我支持您的回答。在Python中,如果my_function没有完全返回两个值且没有任何断言,则第二项测试也会失败-因为赋值会导致异常。因此,实际上不需要使用多个断言和两个检查来测试同一件事。
布朗

@DocBrown-我做了很多假设,但是我不熟悉Python的错误消息,以了解断言失败而不是出于信息原因的随机异常/错误是否有价值。
Telastyn 2015年

我认为在当前情况下,错误消息可能会提供足够的信息。但总的来说,情况可能并非如此。这就是为什么我同意您的看法,对于这种情况,一般的方法应该是使用两个断言来考虑“合并”。如果事实证明可以通过省略肯定的一项进一步简化测试,那很好。
Doc Brown

5
@DocBrown使用断言显式检查的另一个价值是,检查该测试的任何人都清楚这是被测代码的失败,而不是测试本身。当我的测试遇到意外的异常时,我总是不得不花一些时间来找出哪一个实际上是错误的。
克里斯·海斯

@ChrisHayes:我在其中写了“没有需要”,而不是“没有价值”。
布朗

5

您的测试应该是明确的。不能完全推断出text_length如果test_order失败。

我不确定在您发布的Python中它如何运行,但是如果len(result)为3,则第一个将失败,但第二个可能会通过(如果不是在Python中,那么肯定会在JavaScript之类的语言中使用)。

就像您说的那样,您想测试函数是否返回两个数字并且它们是否顺序正确。是两个测试。


1
It's not completely inferred that if text_length fails test_order fails-在Python中,它将给您一个例外。
布朗

我认为问题是关于失败test_length意味着失败的情况test_order。一对用Javascript编写的相似测试不会表现出与这两个python测试相同的事实,这是无关紧要的。
达伍德说恢复莫妮卡

2
@DavidWallace:记住,问题是“编写test_length还是跳过它是一种最佳实践”?在Javascript中,您需要两个测试,以确保您没有错过任何问题(当您不将两个测试合并为一个时)。在Python中,您可以安全地省略第一个(在该答案的第一句话中被拒绝,而在第二个答案中声明为“我不确定”)。老实说,一个好的答案看起来不一样。但是,我没有拒绝这个答案,因为好的部分实际上是提到了JavaScript。
布朗

4
@DavidWallace:因为我们是程序员,而不是stackoverflow.com,所以恕我直言,给出一个可以应用于多种语言的答案要好于仅针对特定语言的答案。但是,当指代特定语言时,答案应该是关于该语言的正确答案,而不是混淆某些属性。
布朗

1
问题是,“是否值得编写一个属于另一个测试的子集的测试”,答案是“在某些语言中,您的两个测试都不是另一个测试的子集”,然后得出结论:两个测试都是因此是必要的。它给我的感觉就像是有人在说:“您的代码根本无法在C ++中编译,此外,在C ++中,一个函数只能返回一个值,因此您无需测试它是否返回两个值。只需编写一个测试”。 ;-)
史蒂夫·杰索普

2

这里的唯一值test_length是,如果一切顺利,则它的存在指示“长度”已经过测试。

因此,确实没有必要同时进行这两个测试。只保留,test_order但考虑重命名test_length_and_order

顺便说一句,我发现名称的使用test有点笨拙。我是测试名称的坚决拥护者,这些名称实际上描述了您所主张的条件。


1
test疣是有意义的Python的测试框架。当然,无论您是否喜欢它,都不需要更改,但是确实会改变阻止某人使用它的论据的分量;-)
Steve Jessop 2015年

@SteveJessop是的,我一直忘记需要它。前一段时间编写Python测试时,我养成了以test_that_,诸如此类开始所有测试的习惯test_that_return_values_are_in_order。也许将来的测试框架版本将以某种方式解决此需求。
达伍德说恢复莫妮卡

@DavidWallace:您可以根据需要更改前缀
Lie Ryan

1

如果那是它们演变成的样子,我将把它们分开。是test_order的,无论何时test_length都可能失败,但是您绝对知道原因。

我也同意,如果test_order首先出现并且您发现可测试的失败是结果可能不是两个值,则将该检查添加为an assert并将测试重命名为test_length_and_order有意义的。

如果您还必须检查返回的值的类型是整数,那么我将在此“综合”结果测试中包括该值。

但请注意,现在您已经对的结果进行了一系列测试my_function()。如果要测试多个上下文(或更可能是多个参数),则可以将其作为子例程来测试的所有结果my_function()

但是,当我编写单元测试时,我通常会分别将边缘和不良情况与良好的输入和正常的输出进行测试(部分原因是我的大多数“单元”测试通常是小型集成测试),并且经常具有多个断言,这些断言只会被破坏如果它们以我发现我需要更多信息而无需调试的方式失败,则将其分为多个单独的测试。

因此,我可能会先从您单独的测试开始,然后扩展test_lengthtest_length_and_types并保留test_order单独的内容,假设后者被视为“正常处理”。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.