6.4. 失败目的测试

测试我们的函数在给出好的输入时是成功的,这是不够的。我们还必须测试在给出坏的输入时它们应失败。并且不只是简单地失败,它们必须按我们期望的方式失败。

回想我们对 toRoman 其它的需求

  1. 当所给的整数超出 13999 时,toRoman 应该失败。
  2. 当给出一个非整数时,toRoman 应该失败。

在Python中,函数通过引发异常指出失败,同时 unittest 模块提供了函数,可以当给出坏的输入时测试函数是否引发一个特定异常。

例 6.3. 对 toRoman 测试坏的输入


class ToRomanBadInput(unittest.TestCase):                            
    def testTooLarge(self):                                          
        """toRoman should fail with large input"""                   
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, 4000) 1

    def testZero(self):                                              
        """toRoman should fail with 0 input"""                       
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, 0)    2

    def testNegative(self):                                          
        """toRoman should fail with negative input"""                
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, -1)  

    def testDecimal(self):                                           
        """toRoman should fail with non-integer input"""             
        self.assertRaises(roman.NotIntegerError, roman.toRoman, 0.5)  3
1

unittestTestCase 类提供了 assertRaises 方法,它接收下面的参数:我们正期待的异常、我们正在测试的函数和我们传给函数的参数。(如果我们正在测试的函数使用一个以上的参数,按顺序把它们全部传给 assertRaises,这个函数将把它们正确地传给我们正在测试的函数。)请仔细观察这里我们所做的:没有直接调用 toRoman 并且手工检查它引发的特殊异常(通过将它包括在try...except中),assertRaises 为我们都封装好了。我们只需要给出指定的异常(roman.OutOfRangeError),函数(toRoman),和 toRoman 的参数(4000) 即可,assertRaises 会小心地调用 toRoman 并且检查以确定它引发了 roman.OutOfRangeError。(最近我说过在Python中每个东西都是对象,包括函数和异常,是多么有用吗?)

2

与测试太大数一起,我们需要测试太小数。回想一下,罗马数字不能表示 0 或负数,所以我们对每种情况都有一个测试用例(testZerotestNegative)。在 testZero 中,我们在测试当使用 0 来调用 toRoman 时将引发 roman.OutOfRangeError 异常;如果它没有引发 roman.OutOfRangeError (或者是因为它返回一个实际值,或因为它引发了别的什么异常),这个测试被认为失败。

3

需求 #3 指出:toRoman 不能接受非整数的小数,所以这里我们测试当使用一个小数(0.5)时,确定 toRoman 应引发 roman.NotIntegerError 异常。如果 toRoman 没有引发 roman.NotIntegerError ,这个测试被认为失败。

后面两个需求同前三个相似,除了它们用 fromRoman 代替了 toRoman

  1. fromRoman 应该接收一个有效的罗马数字并返回它表示的数值。
  2. fromRoman 应该在给出一个无效罗马数字时失败。

需求 #4 按需求 #1的方式进行处理,对已知值的样本进行遍历,按顺序进行测试。需求#5按需求#2和#3的方式进行处理,通过对一系列坏的输入进行测试,确定 fromRoman 引发合适的异常。

例 6.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) 1

    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 oman.NotIntegerError)。在本章的后面,当实际开始编写 roman.py 时,我们将看到如何定义这些定制异常。