第 15 章 重构

15.1. 处理 bugs

尽管你很努力地编写全面的单元测试,但是 bug 还是会出现。我所说的 “bug” 是什么呢?Bug 是你还没有编写的测试用例。

例 15.1. 关于 Bug

>>> import roman5
>>> roman5.fromRoman("") 
0
在前面的章节中你注意到一个空字符串会匹配上那个检查罗马数字有效性的正则表达式了吗?对于最终版本中的正则表达式这一点仍然没有改变。这就是一个 Bug ,你希望空字符串能够像其他无效的罗马数字表示一样引发 InvalidRomanNumeralError 异常。

在重现这个 Bug 并修改它之前你应该编写一个会失败的测试用例来说明它。

例 15.2. 测试 bug (romantest61.py)


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 并确保它会引发一个 InvalidRomanNumeralError 异常。难点在于找出 Bug,既然你已经知道它了,测试就简单了。

因为你的代码存在一个 Bug,并且你编写了测试这个 Bug 的测试用例,所以测试用例将会失败:

例 15.3. 用 romantest61.py 测试 roman61.py 的结果

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)

现在 你可以修改这个 Bug了。

例 15.4. 修改 Bug (roman62.py)

这个文件可以在例子目录下的 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
只需要两行代码:一行直接检查空字符串和一行 raise 语句。

例 15.5. 用 romantest62.py 测试 roman62.py 的结果

fromRoman should only accept uppercase input ... ok
toRoman should always return uppercase ... ok
fromRoman should fail with blank string ... ok 
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

----------------------------------------------------------------------
Ran 13 tests in 2.834s

OK 
空字符串测试用例现在通过了,时时彩计划软件公式:说明 Bug 被修正了。
所有其他测试用例依然通过,证明这个 Bug 修正没有影响到其他部分。不需要再编程了。

这样编程,并没有令 Bug 修正变得简单。简单的 Bug (就像这一个) 需要简单的测试用例,复杂 Bug 则需要复杂的测试用例。以测试为核心的氛围好像 延长了修正 Bug 的时间,因为你需要先贴切地描述出 Bug (编写测试用例) 然后才去修正它。如果测试用例没能正确通过,你需要思量这个修改错了还是测试用例本身出现了 Bug。无论如何,从长远上讲,这样在测试代码和代码之间的反复是值得的,因为这样会使 Bug 在第一时间就被修正的可能性大大提高。而且不论如何更改,你都可以轻易地重新运行所有 测试用例,新代码破坏老代码的机会也变得微乎其微。今天的单元测试就是明天的回归测试 (regression test)。

时时彩稳赚技巧 时时彩娱乐平台有哪些 江西时时彩软件预测 时时彩软件如何调公式 时时彩pk10开奖记录
红太阳娱乐时时彩 新时时彩能中奖吗 时时彩代理收入%是多少 重庆时时彩苹果 时时彩后三直选技巧
江西时时彩走势求高手帮忙啊 fafafa缩水软件 新亚时时彩平台 时时彩组三全包稳赚 时时彩平台哪个稳定
红树林时时彩旧平台 重庆时时彩大神计划群 时时彩软件哪个最垃圾 时时彩5星中合走势图 重庆时时彩五星定胆
时时彩后一技巧 香港赛马会信誉如何 北京赛车改单是真的吗 哈尔滨麻将 时时彩计划软件
天津11选5开奖结果 北京pk10历史开奖直播 浙江快乐12玩法 北京赛车pk10结果 广东十一选五彩票
时时彩平台出租 辽宁11选5 河北十一选五开奖走势 福彩3d分析预测 快3开奖结果走势图
黑龙江11选5走势图专家预测彩乐乐 新疆35选7的开奖号 内蒙古时时彩软件下载 山西十一选五加奖日期 时时彩网站制作