导航: 起始页 > Dive Into Python > 以测试优先为原则的编程 | << >> | ||||
Python 研究(Dive Into Python)Python 从新手到高手 [DIP_5_4_CPUG_RELEASE] |
到目前为止,单元测试已经完成,是时候开始编写被单元测试测试的代码的时候了。你将分阶段地完成这个工作,因此开始时所有的单元测试都是失败的,但在逐步完成 roman.py 的同时你会看到它们一个个地通过(测试) 。
例 14.1. roman1.py
这个程序可以在例子目录下的 py/roman/stage1/ 目录中找到。
如果您还没有下载本书附带的例子程序, 可以 下载本程序和其他例子程序。
"""Convert to and from Roman numerals""" #Define exceptions class RomanError(Exception): pass class OutOfRangeError(RomanError): pass class NotIntegerError(RomanError): pass class InvalidRomanNumeralError(RomanError): pass def toRoman(n): """convert integer to Roman numeral""" pass def fromRoman(s): """convert Roman numeral to integer""" pass
这是如何定义你自己的 Python 异常。异常也是类,通过继承已有的异常,你可以创建自定义的异常。 强烈建议(但不是必须)你继承 Exception 来定义自己的异常,因为它是所有内建异常的基类。 这里我定义了 RomanError (从 Exception 继承而来)作为我所有自定义异常的基类。 这是一个风格问题,我也可以直接从 Exception 继承建立每一个自定义异常。 | |
OutOfRangeError 和 NotIntegerError 异常将会最终被用于 toRoman 以标示不同类型的无效输入,更具体而言就是 ToRomanBadInput 测试的那些。 | |
InvalidRomanNumeralError 将被最终用于 fromRoman 以标示无效输入,具体而言就是 FromRomanBadInput测试的那些。 | |
在这一步中你只是想定义每个函数的 API ,而不想具体实现它们,因此你以 Python 关键字 pass 姑且带过。 |
重要的时刻到了(请打起鼓来):你终于要对这个简陋的小模块开始运行单元测试了。 目前而言,每一个测试用例都应该失败。 事实上,任何测试用例在此时通过,你都应该回头看看 romantest.py ,仔细想想为什么你写的测试代码如此没用,以至于连什么都不作的函数都能通过测试。
运行 romantest1.py 带有 -v 选项的命令行可以得到更详细的输出信息,这样你就可以看到每一个测试用例的具体运行情况。如果幸运,你的结果应该是这样的:
例 14.2. Output of romantest1.py against roman1.py
fromRoman should only accept uppercase input ... ERROR toRoman should always return uppercase ... ERROR fromRoman should fail with malformed antecedents ... FAIL fromRoman should fail with repeated pairs of numerals ... FAIL fromRoman should fail with too many repeated numerals ... FAIL fromRoman should give known result with known input ... FAIL toRoman should give known result with known input ... FAIL fromRoman(toRoman(n))==n for all n ... FAIL toRoman should fail with non-integer input ... FAIL toRoman should fail with negative input ... FAIL toRoman should fail with large input ... FAIL toRoman should fail with 0 input ... FAIL ====================================================================== ERROR: fromRoman should only accept uppercase input ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 154, in testFromRomanCase roman1.fromRoman(numeral.upper()) AttributeError: 'None' object has no attribute 'upper' ====================================================================== ERROR: toRoman should always return uppercase ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 148, in testToRomanCase self.assertEqual(numeral, numeral.upper()) AttributeError: 'None' object has no attribute 'upper' ====================================================================== FAIL: fromRoman should fail with malformed antecedents ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 133, in testMalformedAntecedent self.assertRaises(roman1.InvalidRomanNumeralError, roman1.fromRoman, s) File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises raise self.failureException, excName AssertionError: InvalidRomanNumeralError ====================================================================== FAIL: fromRoman should fail with repeated pairs of numerals ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 127, in testRepeatedPairs self.assertRaises(roman1.InvalidRomanNumeralError, roman1.fromRoman, s) File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises raise self.failureException, excName AssertionError: InvalidRomanNumeralError ====================================================================== FAIL: fromRoman should fail with too many repeated numerals ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 122, in testTooManyRepeatedNumerals self.assertRaises(roman1.InvalidRomanNumeralError, roman1.fromRoman, s) File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises raise self.failureException, excName AssertionError: InvalidRomanNumeralError ====================================================================== FAIL: fromRoman should give known result with known input ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 99, in testFromRomanKnownValues self.assertEqual(integer, result) File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual raise self.failureException, (msg or '%s != %s' % (first, second)) AssertionError: 1 != None ====================================================================== FAIL: toRoman should give known result with known input ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 93, in testToRomanKnownValues self.assertEqual(numeral, result) File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual raise self.failureException, (msg or '%s != %s' % (first, second)) AssertionError: I != None ====================================================================== FAIL: fromRoman(toRoman(n))==n for all n ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 141, in testSanity self.assertEqual(integer, result) File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual raise self.failureException, (msg or '%s != %s' % (first, second)) AssertionError: 1 != None ====================================================================== FAIL: toRoman should fail with non-integer input ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 116, in testNonInteger self.assertRaises(roman1.NotIntegerError, roman1.toRoman, 0.5) File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises raise self.failureException, excName AssertionError: NotIntegerError ====================================================================== FAIL: toRoman should fail with negative input ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 112, in testNegative self.assertRaises(roman1.OutOfRangeError, roman1.toRoman, -1) File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises raise self.failureException, excName AssertionError: OutOfRangeError ====================================================================== FAIL: toRoman should fail with large input ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 104, in testTooLarge self.assertRaises(roman1.OutOfRangeError, roman1.toRoman, 4000) File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises raise self.failureException, excName AssertionError: OutOfRangeError ====================================================================== FAIL: toRoman should fail with 0 input ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 108, in testZero self.assertRaises(roman1.OutOfRangeError, roman1.toRoman, 0) File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises raise self.failureException, excName AssertionError: OutOfRangeError ---------------------------------------------------------------------- Ran 12 tests in 0.040s FAILED (failures=10, errors=2)
<< 完备性检测(Testing for sanity) |
| 1 | 2 | 3 | 4 | 5 | |
roman.py, 第 2 阶段 >> |