13.4. 正面测试 (Testing for success)

单元测试的基础是构建独立的测试用例 (test case)。一个测试用例只回答一个关于被测试代码的问题。

一个测试用例应该做到:

基于如上原则,让我们构建第一个测试用例。应符合如下要求

  1. toRoman 应该为所有 13999 的整数返回罗马数字表示。

例 13.2. testToRomanKnownValues


class KnownValues(unittest.TestCase):                           1
    knownValues = ( (1, 'I'),
                    (2, 'II'),
                    (3, 'III'),
                    (4, 'IV'),
                    (5, 'V'),
                    (6, 'VI'),
                    (7, 'VII'),
                    (8, 'VIII'),
                    (9, 'IX'),
                    (10, 'X'),
                    (50, 'L'),
                    (100, 'C'),
                    (500, 'D'),
                    (1000, 'M'),
                    (31, 'XXXI'),
                    (148, 'CXLVIII'),
                    (294, 'CCXCIV'),
                    (312, 'CCCXII'),
                    (421, 'CDXXI'),
                    (528, 'DXXVIII'),
                    (621, 'DCXXI'),
                    (782, 'DCCLXXXII'),
                    (870, 'DCCCLXX'),
                    (941, 'CMXLI'),
                    (1043, 'MXLIII'),
                    (1110, 'MCX'),
                    (1226, 'MCCXXVI'),
                    (1301, 'MCCCI'),
                    (1485, 'MCDLXXXV'),
                    (1509, 'MDIX'),
                    (1607, 'MDCVII'),
                    (1754, 'MDCCLIV'),
                    (1832, 'MDCCCXXXII'),
                    (1993, 'MCMXCIII'),
                    (2074, 'MMLXXIV'),
                    (2152, 'MMCLII'),
                    (2212, 'MMCCXII'),
                    (2343, 'MMCCCXLIII'),
                    (2499, 'MMCDXCIX'),
                    (2574, 'MMDLXXIV'),
                    (2646, 'MMDCXLVI'),
                    (2723, 'MMDCCXXIII'),
                    (2892, 'MMDCCCXCII'),
                    (2975, 'MMCMLXXV'),
                    (3051, 'MMMLI'),
                    (3185, 'MMMCLXXXV'),
                    (3250, 'MMMCCL'),
                    (3313, 'MMMCCCXIII'),
                    (3408, 'MMMCDVIII'),
                    (3501, 'MMMDI'),
                    (3610, 'MMMDCX'),
                    (3743, 'MMMDCCXLIII'),
                    (3844, 'MMMDCCCXLIV'),
                    (3888, 'MMMDCCCLXXXVIII'),
                    (3940, 'MMMCMXL'),
                    (3999, 'MMMCMXCIX'))                        2

    def testToRomanKnownValues(self):                           3
        """toRoman should give known result with known input"""
        for integer, numeral in self.knownValues:              
            result = roman.toRoman(integer)                     4 5
            self.assertEqual(numeral, result)                   6
1 编写测试用例的第一步就是继承 unittest 模块中的 TestCase 类,它提供了很多可以用在你的测试用例中来测试特定情况的有用方法。
2 这是我手工转换的一个 integer/numeral 对列表。它包含了最小的十个数、最大的数、每个单字符罗马数字对应的数,以及其他随机挑选的有效数样本。单元测试的关键不在于所有可能的输入,而是一个有代表性的样本。
3 每个独立测试本身都是一个方法,既不需要参数也不返回任何值。如果该方法正常退出没有引发异常,测试被认为通过;如果测试引发异常,测试被认为失败。
4 这里你真正调用了 toRoman 函数。(当然,函数还没有编写,但一旦被编写,这里便是调用之处。) 注意你在这里为 toRoman 函数定义了 API :它必须接受整数 (待转换的数) 并返回一个字符串 (对应的罗马数字表示),如果 API 不是这样,测试将失败。
5 同样值得注意,你在调用 toRoman 时没有试图捕捉任何可能发生的异常。这正是我们所希望的。以有效输入调用 toRoman 不会引发任何异常,而你看到的这些输入都是有效的。如果 toRoman 引发了异常,则测试失败。
6 假设 toRoman 函数被正确编写,正确调用,运行成功并返回一个值,最后一步便是检查这个返回值正确 与否。这是一个常见的问题,TestCase 类提供了一个方法:assertEqual,来测试两个值是否相等。如果 toRoman 返回的结果 (value) 不等于我们预期的值 (numeral),assertEqual 将会引发一个异常,测试也就此失败。如果两个值相等,assertEqual 什么也不做。如果每个从 toRoman 返回的值都等于预期值,assertEqual 便不会引发异常,于是 testToRomanKnownValues 最终正常退出,这意味着 toRoman 通过了该测试。