Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 53 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
53
Dung lượng
180,21 KB
Nội dung
Chapter 14.Test-FirstProgramming 14.1. roman.py, stage 1 Now that the unit tests are complete, it's time to start writing the code that the test cases are attempting to test. You're going to do this in stages, so you can see all the unit tests fail, then watch them pass one by one as you fill in the gaps in roman.py. Example 14.1. roman1.py This file is available in py/roman/stage1/ in the examples directory. If you have not already done so, you can download this and other examples used in this book. """Convert to and from Roman numerals""" #Define exceptions class RomanError(Exception): pass 1 class OutOfRangeError(RomanError): pass 2 class NotIntegerError(RomanError): pass class InvalidRomanNumeralError(RomanError): pass 3 def toRoman(n): """convert integer to Roman numeral""" pass 4 def fromRoman(s): """convert Roman numeral to integer""" pass 1 This is how you define your own custom exceptions in Python. Exceptions are classes, and you create your own by subclassing existing exceptions. It is strongly recommended (but not required) that you subclass Exception, which is the base class that all built-in exceptions inherit from. Here I am defining RomanError (inherited from Exception) to act as the base class for all my other custom exceptions to follow. This is a matter of style; I could just as easily have inherited each individual exception from the Exception class directly. 2 The OutOfRangeError and NotIntegerError exceptions will eventually be used by toRoman to flag various forms of invalid input, as specified in ToRomanBadInput. 3 The InvalidRomanNumeralError exception will eventually be used by fromRoman to flag invalid input, as specified in FromRomanBadInput. 4 At this stage, you want to define the API of each of your functions, but you don't want to code them yet, so you stub them out using the Python reserved word pass. Now for the big moment (drum roll please): you're finally going to run the unit test against this stubby little module. At this point, every test case should fail. In fact, if any test case passes in stage 1, you should go back to romantest.py and re-evaluate why you coded a test so useless that it passes with do-nothing functions. Run romantest1.py with the -v command-line option, which will give more verbose output so you can see exactly what's going on as each test case runs. With any luck, your output should look like this: Example 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 [...]... was a failure, because the call to fromRoman did not raise the InvalidRomanNumeral exception that assertRaises was looking for 14.2 roman.py, stage 2 Now that you have the framework of the roman module laid out, it's time to start writing code and passing test cases Example 14.3 roman2.py This file is available in py/roman/stage2/ in the examples directory If you have not already done so, you can download... AssertionError: OutOfRangeError -Ran 12 tests in 0.320s FAILED (failures=10) 14.3 roman.py, stage 3 Now that toRoman behaves correctly with good input (integers from 1 to 3999), it's time to make it behave correctly with bad input (everything else) Example 14.6 roman3.py This file is available in py/roman/stage3/ in the examples directory If you have not already done... adding X to output subtracting 4 from input, adding IV to output 'MCDXXIV' So toRoman appears to work, at least in this manual spot check But will it pass the unit testing? Well no, not entirely Example 14.5 Output of romantest2.py against roman2.py Remember to run romantest2.py with the -v command-line flag to enable verbose mode fromRoman should only accept uppercase input FAIL toRoman should always... value less than or equal to the input Once found, you add the Roman numeral representation to the end of the output, subtract the corresponding integer value from the input, lather, rinse, repeat Example 14.4 How toRoman works If you're not clear how toRoman works, add a print statement to the end of the while loop: while n >= integer: result += numeral n -= integer print 'subtracting', integer, 'from... in the traceback that is printed if the exception is never handled 3 This is the non-integer check Non-integers can not be converted to Roman numerals 4 The rest of the function is unchanged Example 14.7 Watching toRoman handle bad input >>> import roman3 >>> roman3.toRoman(4000) Traceback (most recent call last): File "", line 1, in ? File "roman3.py", line 27, in toRoman raise... last): File "", line 1, in ? File "roman3.py", line 29, in toRoman raise NotIntegerError, "non-integers can not be converted" NotIntegerError: non-integers can not be converted Example 14.8 Output of romantest3.py against roman3.py fromRoman should only accept uppercase input FAIL toRoman should always return uppercase ok fromRoman should fail with malformed antecedents FAIL fromRoman... comprehensive unit testing can tell you is when to stop coding When all the unit tests for a function pass, stop coding the function When all the unit tests for an entire module pass, stop coding the module 14.4 roman.py, stage 4 . Chapter 14. Test-First Programming 14. 1. roman.py, stage 1 Now that the unit tests are complete,. then watch them pass one by one as you fill in the gaps in roman.py. Example 14. 1. roman1.py This file is available in py/roman/stage1/ in the examples directory.