您当前位置:首页 > 深入 Python > 重构 | << >> | ||||
深入 Python从 Python 新手到专家 |
尽管您尽最大努力编写了全面的单元测试,但错误仍然会发生。我所说的“错误”是什么意思?错误是指您尚未编写的测试用例。
>>> import roman5 >>> roman5.fromRoman("")0
![]() |
还记得在上一节中,您一直看到空字符串会匹配您用来检查有效罗马数字的正则表达式吗?事实证明,对于最终版本的正则表达式来说,这仍然是正确的。这是一个错误;您希望空字符串像任何其他不代表有效罗马数字的字符序列一样引发 InvalidRomanNumeralError 异常。 |
在重现错误之后以及修复错误之前,您应该编写一个失败的测试用例,从而说明该错误。
class FromRomanBadInput(unittest.TestCase): # previous test cases omitted for clarity (they haven't changed) def testBlank(self): """fromRoman should fail with blank string""" self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, "")![]()
由于您的代码存在错误,并且您现在有了一个测试该错误的测试用例,因此该测试用例将失败
fromRoman should only accept uppercase input ... ok toRoman should always return uppercase ... ok fromRoman should fail with blank string ... FAIL fromRoman should fail with malformed antecedents ... ok fromRoman should fail with repeated pairs of numerals ... ok fromRoman should fail with too many repeated numerals ... ok fromRoman should give known result with known input ... ok toRoman should give known result with known input ... ok fromRoman(toRoman(n))==n for all n ... ok toRoman should fail with non-integer input ... ok toRoman should fail with negative input ... ok toRoman should fail with large input ... ok toRoman should fail with 0 input ... ok ====================================================================== FAIL: fromRoman should fail with blank string ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage6\romantest61.py", line 137, in testBlank self.assertRaises(roman61.InvalidRomanNumeralError, roman61.fromRoman, "") File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises raise self.failureException, excName AssertionError: InvalidRomanNumeralError ---------------------------------------------------------------------- Ran 13 tests in 2.864s FAILED (failures=1)
现在您可以修复错误了。
此文件位于示例目录的 py/roman/stage6/ 中。
def fromRoman(s): """convert Roman numeral to integer""" if not s:raise InvalidRomanNumeralError, 'Input can not be blank' if not re.search(romanNumeralPattern, s): raise InvalidRomanNumeralError, 'Invalid Roman numeral: %s' % s result = 0 index = 0 for numeral, integer in romanNumeralMap: while s[index:index+len(numeral)] == numeral: result += integer index += len(numeral) return result
fromRoman should only accept uppercase input ... ok toRoman should always return uppercase ... ok fromRoman should fail with blank string ... okfromRoman should fail with malformed antecedents ... ok fromRoman should fail with repeated pairs of numerals ... ok fromRoman should fail with too many repeated numerals ... ok fromRoman should give known result with known input ... ok toRoman should give known result with known input ... ok fromRoman(toRoman(n))==n for all n ... ok toRoman should fail with non-integer input ... ok toRoman should fail with negative input ... ok toRoman should fail with large input ... ok toRoman should fail with 0 input ... ok ---------------------------------------------------------------------- Ran 13 tests in 2.834s OK
以这种方式编码并不能使修复错误变得更容易。简单的错误(如这个错误)需要简单的测试用例;复杂的错误将需要复杂的测试用例。在以测试为中心的环境中,修复错误似乎需要更长的时间,因为您需要在代码中准确地阐述错误是什么(编写测试用例),然后修复错误本身。然后,如果测试用例没有立即通过,您需要弄清楚是修复错误了,还是测试用例本身存在错误。但是,从长远来看,测试代码和被测代码之间的这种来回交互是值得的,因为它可以提高第一次就正确修复错误的可能性。此外,由于您可以轻松地重新运行所有测试用例以及您的新测试用例,因此在修复新代码时,您不太可能破坏旧代码。今天的单元测试就是明天的回归测试。
<< roman.py,阶段 5 |
| 1 | 2 | 3 | 4 | 5 | |
处理需求变更 >> |