当前位置:首页 > 深入 Python > 单元测试 > 健全性测试 | << >> | ||||
深入 Python从 Python 新手到专家 |
通常,您会发现一个代码单元包含一组互逆函数,通常以转换函数的形式出现,其中一个函数将 A 转换为 B,另一个函数将 B 转换为 A。在这些情况下,创建一个“健全性检查”非常有用,以确保您可以将 A 转换为 B 并返回 A,而不会损失精度、产生舍入误差或触发任何其他类型的错误。
考虑此需求
class SanityCheck(unittest.TestCase): def testSanity(self): """fromRoman(toRoman(n))==n for all n""" for integer in range(1, 4000):![]()
numeral = roman.toRoman(integer) result = roman.fromRoman(numeral) self.assertEqual(integer, result)
![]() |
您之前已经见过range 函数,但这里它使用两个参数调用,返回一个整数列表,从第一个参数 (1) 开始,连续计数到第二个参数 (4000),但不包括第二个参数。因此,1..3999 是转换为罗马数字的有效范围。 |
![]() |
我只想顺便提一下,integer 在 Python 中不是关键字;这里它只是一个变量名,和其他变量名一样。 |
![]() |
这里的实际测试逻辑很简单:取一个数字 (integer),将其转换为罗马数字 (numeral),然后将其转换回数字 (result),并确保最终得到的数字与开始时相同。如果不同,assertEqual 将引发异常,测试将立即被视为失败。如果所有数字都匹配,assertEqual 将始终静默返回,整个 testSanity 方法最终将静默返回,测试将被视为通过。 |
最后两个需求与其他需求不同,因为它们看起来既随意又琐碎
事实上,它们在某种程度上是随意的。例如,您可以规定 fromRoman 接受小写和混合大小写输入。但它们并非完全随意;如果 toRoman 始终返回大写输出,则 fromRoman 必须至少接受大写输入,否则“健全性检查”(需求 #6)将失败。它仅接受大写输入的事实是随意的,但正如任何系统集成商都会告诉您的那样,大小写始终很重要,因此最好预先指定行为。如果值得指定,就值得测试。
class CaseCheck(unittest.TestCase): def testToRomanCase(self): """toRoman should always return uppercase""" for integer in range(1, 4000): numeral = roman.toRoman(integer) self.assertEqual(numeral, numeral.upper())def testFromRomanCase(self): """fromRoman should only accept uppercase input""" for integer in range(1, 4000): numeral = roman.toRoman(integer) roman.fromRoman(numeral.upper())
![]()
self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, numeral.lower())
![]() |
这个测试用例最有趣的地方在于它没有测试的所有东西。它没有测试从 toRoman 返回的值是否正确,甚至没有测试它是否一致;这些问题由单独的测试用例回答。您有一个完整的测试用例只是为了测试大写字母。您可能会想将其与健全性检查结合起来,因为两者都遍历了整个值范围并调用了 toRoman。[6] 但这将违反基本规则之一:每个测试用例应该只回答一个问题。想象一下,您将此大小写检查与健全性检查结合起来,然后该测试用例失败了。您需要进行进一步的分析,以找出测试用例的哪一部分失败了,才能确定问题所在。如果您需要分析单元测试的结果才能弄清楚它们的含义,那么这肯定表明您对测试用例的设计有误。 |
![]() |
这里有一个类似的教训需要学习:即使“您知道”toRoman 始终返回大写字母,但您在这里还是明确地将其返回值转换为大写字母,以测试 fromRoman 是否接受大写输入。为什么?因为 toRoman 始终返回大写字母这一事实是一个独立的需求。如果您更改了该需求,例如,它始终返回小写字母,则 testToRomanCase 测试用例需要更改,但此测试用例仍然有效。这是基本规则的另一个:每个测试用例必须能够独立于任何其他测试用例工作。每个测试用例都是一个孤岛。 |
![]() |
请注意,您没有将 fromRoman 的返回值赋给任何变量。这是 Python 中的合法语法;如果一个函数返回一个值,但没有人监听,Python 就会丢弃该返回值。在本例中,这正是您想要的。此测试用例不测试返回值的任何内容;它只是测试 fromRoman 是否接受大写输入而不引发异常。 |
![]() |
这是一行复杂的代码,但它与您在 ToRomanBadInput 和 FromRomanBadInput 测试中所做的非常相似。您正在测试以确保使用特定值(numeral.lower(),循环中当前罗马数字的小写版本)调用特定函数(roman.fromRoman)会引发特定异常(roman.InvalidRomanNumeralError)。如果它确实如此(每次循环都如此),则测试通过;如果即使有一次它做了其他事情(例如引发了不同的异常,或者在没有引发任何异常的情况下返回了一个值),则测试失败。 |
在下一章中,您将看到如何编写通过这些测试的代码。
[6] “我能抗拒一切,除了诱惑。”——奥斯卡·王尔德
<< 测试失败 |
| 1 | 2 | 3 | 4 | 5 | 6 | |
测试优先编程 >> |