1. Trang chủ
  2. » Công Nghệ Thông Tin

Dive Into Python-Chapter 14. Test-First Programming

53 367 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Test-first programming
Thể loại Chương
Định dạng
Số trang 53
Dung lượng 180,21 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

--- Traceback most recent call last: File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 133, in testMalformedAntecedent self.assertRaisesroman1.InvalidRomanNumeralError, roman1.

Trang 1

Chapter 14 Test-First Programming

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

Trang 2

class RomanError(Exception): pass 1

class OutOfRangeError(RomanError): pass 2

class NotIntegerError(RomanError): pass

class InvalidRomanNumeralError(RomanError): pass 3

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

Trang 3

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

Trang 4

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

Trang 5

-

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 154, in

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 148, in

Trang 6

-

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

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 127, in

Trang 7

raise self.failureException, excName

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

Trang 8

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))

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

Trang 9

================

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))

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 116, in

testNonInteger

Trang 10

self.assertRaises(roman1.NotIntegerError, roman1.toRoman, 0.5)

File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises

raise self.failureException, excName

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

Trang 11

-

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

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 2

Trang 12

-

Ran 12 tests in 0.040s 3

FAILED (failures=10, errors=2) 4

1 Running the script runs unittest.main(), which runs each test case, which is to say each method defined in each class within romantest.py For each test case, it prints out the doc string of the method and whether that test passed or failed As expected, none of the test cases passed

2 For each failed test case, unittest displays the trace information

showing exactly what happened In this case, the call to assertRaises (also called failUnlessRaises) raised an AssertionError because it was expecting toRoman to raise an OutOfRangeError and it didn't

3 After the detail, unittest displays a summary of how many tests were performed and how long it took

4 Overall, the unit test failed because at least one test case did not pass When a test case doesn't pass, unittest distinguishes between failures and errors A failure is a call to an assertXYZ method, like assertEqual or

assertRaises, that fails because the asserted condition is not true or the

expected exception was not raised An error is any other sort of exception raised in the code you're testing or the unit test case itself For instance, the testFromRomanCase method (“fromRoman should only accept uppercase

Trang 13

input”) was an error, because the call to numeral.upper() raised an

AttributeError exception, because toRoman was supposed to return a string but didn't But testZero (“toRoman should fail with 0 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 this and other examples used in this book

"""Convert to and from Roman numerals"""

#Define exceptions

Trang 14

class RomanError(Exception): pass

class OutOfRangeError(RomanError): pass

class NotIntegerError(RomanError): pass

class InvalidRomanNumeralError(RomanError): pass

#Define digit mapping

Trang 16

1 The character representations of the most basic Roman numerals Note that this is not just the single-character Roman numerals; you're also

defining two-character pairs like CM (“one hundred less than one

thousand”); this will make the toRoman code simpler later

2 The order of the Roman numerals They are listed in descending value order, from M all the way down to I

3 The value of each Roman numeral Each inner tuple is a pair of

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:

Trang 17

subtracting 1000 from input, adding M to output

subtracting 400 from input, adding CD to output

subtracting 10 from input, adding X to output

subtracting 10 from input, adding X to output

subtracting 4 from input, adding IV to output

Trang 18

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 return uppercase ok 1

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 ok 2

fromRoman(toRoman(n))==n for all n FAIL

toRoman should fail with non-integer input FAIL 3

toRoman should fail with negative input FAIL

toRoman should fail with large input FAIL

toRoman should fail with 0 input FAIL

Trang 19

1 toRoman does, in fact, always return uppercase, because

romanNumeralMap defines the Roman numeral representations as

uppercase So this test passes already

2 Here's the big news: this version of the toRoman function passes the known values test Remember, it's not comprehensive, but it does put the function through its paces with a variety of good inputs, including inputs that produce every single-character Roman numeral, the largest possible input (3999), and the input that produces the longest possible Roman numeral (3888) At this point, you can be reasonably confident that the function

works for any good input value you could throw at it

3 However, the function does not “work” for bad values; it fails every single bad input test That makes sense, because you didn't include any

checks for bad input Those test cases look for specific exceptions to be raised (via assertRaises), and you're never raising them You'll do that in the next stage

Here's the rest of the output of the unit test, listing the details of all the

failures You're down to 10

======================================================

================

Trang 20

FAIL: fromRoman should only accept uppercase input

-

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 156, in

testFromRomanCase

roman2.fromRoman, numeral.lower())

File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises

raise self.failureException, excName

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 133, in

testMalformedAntecedent

self.assertRaises(roman2.InvalidRomanNumeralError,

roman2.fromRoman, s)

Trang 21

File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises

raise self.failureException, excName

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 127, in

testRepeatedPairs

self.assertRaises(roman2.InvalidRomanNumeralError,

roman2.fromRoman, s)

File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises

raise self.failureException, excName

Trang 22

-

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 122, in

testTooManyRepeatedNumerals

self.assertRaises(roman2.InvalidRomanNumeralError,

roman2.fromRoman, s)

File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises

raise self.failureException, excName

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 99, in

testFromRomanKnownValues

self.assertEqual(integer, result)

File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual

Trang 23

raise self.failureException, (msg or '%s != %s' % (first, second))

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage2\romantest2.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))

Trang 24

File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 116, in

testNonInteger

self.assertRaises(roman2.NotIntegerError, roman2.toRoman, 0.5)

File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises

raise self.failureException, excName

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 112, in

testNegative

self.assertRaises(roman2.OutOfRangeError, roman2.toRoman, -1)

File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises

raise self.failureException, excName

AssertionError: OutOfRangeError

Trang 25

================

FAIL: toRoman should fail with large input

-

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 104, in

testTooLarge

self.assertRaises(roman2.OutOfRangeError, roman2.toRoman, 4000)

File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises

raise self.failureException, excName

Traceback (most recent call last):

File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 108, in testZero

self.assertRaises(roman2.OutOfRangeError, roman2.toRoman, 0)

Trang 26

File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises

raise self.failureException, excName

This file is available in py/roman/stage3/ in the examples directory

If you have not already done so, you can download this and other examples used in this book

Trang 27

"""Convert to and from Roman numerals"""

#Define exceptions

class RomanError(Exception): pass

class OutOfRangeError(RomanError): pass

class NotIntegerError(RomanError): pass

class InvalidRomanNumeralError(RomanError): pass

#Define digit mapping

Ngày đăng: 20/10/2013, 10:15

TỪ KHÓA LIÊN QUAN