13.6. 完备性检测 (Testing for sanity)

你经常会发现一组代码中包含互逆的转换函数,一个把 A 转换为 B ,另一个把 B 转换为 A。在这种情况下,创建“完备性检测”可以使你在由 A 转 B 再转 A 的过程中不会出现丢失精度或取整等错误。

考虑这个要求

  1. 如果你给定一个数,把它转化为罗马数字表示,然后再转换回阿拉伯数字表示,你所得到的应该是最初给定的那个数。因此,对于 1..3999 中的nfromRoman(toRoman(n)) == n 总成立。

例 13.5. 以 toRoman 测试 fromRoman 的输出


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 方法将会最终也保持沉默,测试则将会被认定为通过。

最后两个要求和其他的要求不同,似乎既武断而又微不足道:

  1. toRoman 返回的罗马数字应该使用大写字母。
  2. fromRoman 应该只接受大写罗马数字 (也就是说给定小写字母进行转换时应该失败)。

事实上,时时彩计划软件公式:它们确实有点武断,譬如你完全可以让 fromRoman 接受小写和大小写混合的输入;但他们也不是完全武断;如果 toRoman 总是返回大写的输出,那么 fromRoman 至少应该接受大写字母输入,不然 “完备性检测” (要求 #6) 就会失败。不管怎么说, 接受大写输入还是武断的,但就像每个系统都会告诉你的那样,大小写总会出问题,因此事先规定这一点还是有必要的。既然有必要规定,那么也就有必要测试。

例 13.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[11]但是这样将会违背一条基本规则:每个测试用例只回答一个问题。试想一下,你将这个测试并入到完备性测试中,然后遇到了测试失败。你还需要进一步分析以便判定测试用例的哪部分出了问题。如果你需要分析方能找出问题所在,无疑你的测试用例在设计上出了问题。
这有一个和前面相似的情况:尽管 “你知道toRoman 总是返回大写字母,你还是需要把返回值显式地转换成大写字母后再传递给只接受大写的 fromRoman 进行测试。为什么?因为 toRoman 只返回大写字母是一个独立的需求。如果你改变了这个需求,例如改成总是返回小写字母,那么 testToRomanCase 测试用例也应作出调整,但这个测试用例应该仍能通过。这是另外一个基本规则:每个测试用例必须可以与其他测试用例隔离工作,每个测试用例是一个“孤岛”。
注意你并没有使用 fromRoman 的返回值。这是一个有效的 Python 语法:如果一个函数返回一个值,但没有被使用,Python 会直接把这个返回值扔掉。这正是你所希望的,这个测试用例并不对返回值进行测试,只是测试 fromRoman 接受大写字母而不引发异常。
这行有点复杂,但是它与 ToRomanBadInputFromRomanBadInput 测试很相似。 你在测试以特定值 (numeral.lower(),循环中目前罗马数字的小写版) 调用特定函数 (roman.fromRoman) 会确实引发特定的异常 (roman.InvalidRomanNumeralError)。如果 (在循环中的每一次) 确实如此,测试通过;如果有一次不是这样 (比如引发另外的异常或者不引发异常),测试失败。

在下一章中,你将看到如何编写可以通过这些测试的代码。

Footnotes

[11] 除了诱惑什么我都能抗拒。 (I can resist everything except temptation.)”――Oscar Wilde

重庆时时彩人工后一 重庆时时彩易购娱乐 机器人时时彩插件 时时彩五星一码不定位 拉菲时时彩可靠吗
时时彩九宫图杀号法 时时彩的充值时间 时时彩注册平台 扫描全能王类似软件 时时彩1000期无错杀码
魔方时时计划软件app 财神爷时时彩 重庆时时彩倍头计算器 聚宝盆手机版 17年重庆时时彩骗局
时时彩庄家改号 天天时时彩软件手机版式 优博时时彩平台手机版 时时彩平台信誉排行榜 重庆时时彩有人赢钱吗
福建22选5几点开奖 时时彩软件论坛 河南11选5今日开奖结果走势图 浙江十一选五中奖规则 湖南幸运赛车微信玩发
四川十一选五开奖结果 007 皇家赌场 河北体彩11选5 河南快三开奖结果查询 幸运飞艇微信
18选7今日开奖结果查询 重庆时时彩中奖中多少 贵州十一选五走势图一 青海快3预测计划♀广西快3和值追号计划♀快3跨度和值组合表计划♀北京赛车冠亚和值技巧 赛马会
快3遗漏 吉林快三预测与推荐 黑龙江p6230期走势图 如何减掉大肚腩 必赢客北京pk拾注册码