企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 13.5. 负面测试 (Testing for failure) 使用有效输入确保函数成功通过测试还不够,你还需要测试无效输入导致函数失败的情形。但并不是任何失败都可以,必须如你预期地失败。 还记得 `toRoman` 的[其他要求](index.html#roman.requirements)吧: 1. `toRoman` 在输入值为 `1` 到 `3999` 之外时失败。 2. `toRoman` 在输入值为非整数时失败。 在 Python 中,函数以引发[异常](../file_handling/index.html#fileinfo.exception "6.1. 异常处理")的方式表示失败。`unittest` 模块提供了用于测试函数是否在给定无效输入时引发特定异常的方法。 ## 例 13.3. 测试 `toRoman` 的无效输入 ``` class ToRomanBadInput(unittest.TestCase): def testTooLarge(self): """toRoman should fail with large input""" self.assertRaises(roman.OutOfRangeError, roman.toRoman, 4000) def testZero(self): """toRoman should fail with 0 input""" self.assertRaises(roman.OutOfRangeError, roman.toRoman, 0) def testNegative(self): """toRoman should fail with negative input""" self.assertRaises(roman.OutOfRangeError, roman.toRoman, -1) def testNonInteger(self): """toRoman should fail with non-integer input""" self.assertRaises(roman.NotIntegerError, roman.toRoman, 0.5) ``` | | | | --- | --- | | \[1\] | `unittest` 模块中的 `TestCase` 类提供了 `assertRaises` 方法,它接受这几个参数:预期的异常、测试的函数,以及传递给函数的参数。(如果被测试函数有不止一个参数,把它们按顺序全部传递给 `assertRaises` ,它会把这些参数传给被测的函数。) 特别注意这里的操作:不是直接调用 `toRoman` 再手工查看是否引发特定异常 (使用 [`try...except` 块](../file_handling/index.html#fileinfo.exception "6.1. 异常处理")捕捉异常),`assertRaises` 为我们封装了这些。所有你要做的就是把异常 (`roman.OutOfRangeError`)、函数 (`toRoman`) 以及 `toRoman` 的参数 (`4000`) 传递给 `assertRaises` ,它会调用 `toRoman` 查看是否引发 `roman.OutOfRangeError` 异常。(还应注意到你是把 `toRoman` 函数本身当作一个参数,而不是调用它,传递它的时候也不是把它的名字作为一个字符串。我提到过吗?无论是函数还是异常, [Python 中万物皆对象](../getting_to_know_python/everything_is_an_object.html "2.4. 万物皆对象"))。 | | \[2\] | 与测试过大的数相伴的便是测试过小的数。记住,罗马数字不能表示 `0` 和负数,所以你要分别编写测试用例 ( `testZero` 和 `testNegative`)。在 `testZero` 中,你测试 `toRoman` 调用 `0` 引发的 `roman.OutOfRangeError` 异常,如果_没能_ 引发 `roman.OutOfRangeError` (不论是返回了一个值还是引发了其他异常),则测试失败。 | | \[3\] | [要求 #3](index.html#roman.requirements):`toRoman` 不能接受非整数输入,所以这里你测试 `toRoman` 在输入 `0.5` 时引发 `roman.NotIntegerError` 异常。如果 `toRoman` 没有引发 `roman.NotIntegerError` 异常,则测试失败。 | 接下来的两个[要求](index.html#roman.requirements)与前三个类似,不同点是他们所针对的是 `fromRoman` 而不是 `toRoman`: 1. `fromRoman` 应该能将输入的有效罗马数字转换为相应的阿拉伯数字表示。 2. `fromRoman` 在输入无效罗马数字时应该失败。 要求 #4 与[要求 #1](testing_for_success.html#roman.testtoromanknownvalues.example "例 13.2. testToRomanKnownValues") 的处理方法相同,即测试一个已知样本中的一个个数字对。要求 #5 与 #2 和 #3的处理方法相同,即通过无效输入确认 `fromRoman` 引发恰当的异常。 ## 例 13.4. 测试 `fromRoman` 的无效输入 ``` class FromRomanBadInput(unittest.TestCase): def testTooManyRepeatedNumerals(self): """fromRoman should fail with too many repeated numerals""" for s in ('MMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'): self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s) def testRepeatedPairs(self): """fromRoman should fail with repeated pairs of numerals""" for s in ('CMCM', 'CDCD', 'XCXC', 'XLXL', 'IXIX', 'IVIV'): self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s) def testMalformedAntecedent(self): """fromRoman should fail with malformed antecedents""" for s in ('IIMXCC', 'VX', 'DCM', 'CMM', 'IXIV', 'MCMC', 'XCX', 'IVI', 'LM', 'LD', 'LC'): self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s) ``` | | | | --- | --- | | \[1\] | 没什么新鲜的,与测试 `toRoman` 无效输入时相同的模式,只是你有了一个新的异常:`roman.InvalidRomanNumeralError`。`roman.py` 中一共要定义三个异常 (另外的两个是 `roman.OutOfRangeError` 和 `roman.NotIntegerError`)。稍后你在开始编写 `roman.py` 时将会知道如何定义这些异常。 |