13.5. 测试失败情况

仅仅测试函数在输入正确时成功运行是不够的;您还必须测试它们在输入错误时是否会按预期失败。

请记住 toRoman其他要求

  1. 当输入的整数超出 13999 的范围时,toRoman 应该失败。
  2. 当输入非整数时,toRoman 应该失败。

Python 中,函数通过引发异常 来表示失败,而 unittest 模块提供了用于测试函数在接收到错误输入时是否引发特定异常的方法。

示例 13.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 testNonInteger(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。(还要注意,您正在将 toRoman 函数本身作为参数传递;您没有调用它,也没有将其名称作为字符串传递。我最近是否提到过 Python 中的一切都是对象,包括函数和异常?)
2 除了测试过大的数字之外,还需要测试过小的数字。请记住,罗马数字不能表示 0 或负数,因此您需要为每个数字编写一个测试用例(testZerotestNegative)。在 testZero 中,您正在测试 toRoman 在使用 0 调用时是否引发 roman.OutOfRangeError 异常;如果它没有 引发 roman.OutOfRangeError(因为它返回了一个实际值,或者因为它引发了其他异常),则认为此测试失败。
3 要求 #3 指定 toRoman 不能接受非整数,因此您需要在此处测试以确保 toRoman 在使用 0.5 调用时引发 roman.NotIntegerError 异常。如果 toRoman 没有引发 roman.NotIntegerError,则认为此测试失败。

接下来的两个要求与前三个类似,只是它们适用于 fromRoman 而不是 toRoman

  1. fromRoman 应该接受一个有效的罗马数字并返回它所代表的数字。
  2. 当给定一个无效的罗马数字时,fromRoman 应该失败。

要求 #4 的处理方式与要求 #1相同,遍历已知值的样本并依次测试每个值。要求 #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) 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.OutOfRangeErrorroman.NotIntegerError)。在本章后面实际开始编写 roman.py 时,您将看到如何定义这些自定义异常。