1. Create a new file called recipe10.py in which to put all our code for this recipe.
2. Pick a class to test. In this recipe, we'll use another variation of our Roman numeral
converter. This one doesn't process values greater than 4000.
3. Create a test class to exercise the Roman numeral converter.
4. Write a test method that exercises the edges of the Roman numeral converter.
5. Create a test method that exercises the tiers converting from decimal to Roman
numerals.
6. Create a test method that exercises a set of invalid inputs.
7. Code a utility method that iterates over the edge cases and runs different assertions
based on each edge.
8. Make the script runnable by loading the test case into TextTestRunner.
9. Run the test case.
Code
# !usr/bin/env python 2.7
# coding: utf-8
# filename: recipe10.py
class RomanNumeralConverter(object):
def __init__(self):
self.digit_map = {"M":1000, "D":500, "C":100, "L":50, "X":10, "V":5, "I":1}
def convert_to_decimal(self, roman_numeral):
val = 0
for char in roman_numeral:
val += self.digit_map[char]
if val > 4000:
raise Exception(\
"We don't handle values over 4000")
return val
def convert_to_roman(self, decimal):
if decimal > 4000:
raise Exception(\
"We don't handle values over 4000")
val = ""
mappers = [(1000,"M"), (500,"D"), (100,"C"), (50,"L"), (10,"X"), (5,"V"), (1,"I")]
for (mapper_dec, mapper_rom) in mappers:
while decimal >= mapper_dec:
val += mapper_rom
decimal -= mapper_dec
return val
import unittest
class RomanNumeralTest(unittest.TestCase):
def setUp(self):
self.cvt = RomanNumeralConverter()
def test_edges(self):
r = self.cvt.convert_to_roman
d = self.cvt.convert_to_decimal
edges = [("equals", r, "I", 1),\
("equals", r, "", 0),\
("equals", r, "", -1),\
("equals", r, "MMMM", 4000),\
("raises", r, Exception, 4001),\
("equals", d, 1, "I"),\
("equals", d, 0, ""),\
("equals", d, 4000, "MMMM"),\
("raises", d, Exception, "MMMMI")
]
[self.checkout_edge(edge) for edge in edges]
def test_tiers(self):
r = self.cvt.convert_to_roman
edges = [("equals", r, "V", 5),\
("equals", r, "VIIII", 9),\
("equals", r, "X", 10),\
("equals", r, "XI", 11),\
("equals", r, "XXXXVIIII", 49),\
("equals", r, "L", 50),\
("equals", r, "LI", 51),\
("equals", r, "LXXXXVIIII", 99),\
("equals", r, "C", 100),\
("equals", r, "CI", 101),\
("equals", r, "CCCCLXXXXVIIII", 499),\
("equals", r, "D", 500),\
("equals", r, "DI", 501),\
("equals", r, "M", 1000)\
]
[self.checkout_edge(edge) for edge in edges]
def test_bad_inputs(self):
r = self.cvt.convert_to_roman
d = self.cvt.convert_to_decimal
edges = [("equals", r, "", None),\
("equals", r, "I", 1.2),\
("raises", d, TypeError, None),\
("raises", d, TypeError, 1.2)\
]
[self.checkout_edge(edge) for edge in edges]
def checkout_edge(self, edge):
if edge[0] == "equals":
f, output, input = edge[1], edge[2], edge[3]
print("Converting %s to %s..." % (input, output))
self.assertEquals(output, f(input))
elif edge[0] == "raises":
f, exception, args = edge[1], edge[2], edge[3:]
print("Converting %s, expecting %s" % \
(args, exception))
self.assertRaises(exception, f, *args)
if __name__ == "__main__":
suite = unittest.TestLoader().loadTestsFromTestCase( \
RomanNumeralTest)
unittest.TextTestRunner(verbosity=2).run(suite)
test_bad_inputs (__main__.RomanNumeralTest) ... Converting None to ...
Converting 1.2 to I...
Converting (None,), expecting <type 'exceptions.TypeError'>
Converting (1.2,), expecting <type 'exceptions.TypeError'>
ok
test_edges (__main__.RomanNumeralTest) ... Converting 1 to I...
Converting 0 to ...
Converting -1 to ...
Converting 4000 to MMMM...
Converting (4001,), expecting <type 'exceptions.Exception'>
Converting I to 1...
Converting to 0...
Converting MMMM to 4000...
Converting ('MMMMI',), expecting <type 'exceptions.Exception'>
ok
test_tiers (__main__.RomanNumeralTest) ... Converting 5 to V...
Converting 9 to VIIII...
Converting 10 to X...
Converting 11 to XI...
Converting 49 to XXXXVIIII...
Converting 50 to L...
Converting 51 to LI...
Converting 99 to LXXXXVIIII...
Converting 100 to C...
Converting 101 to CI...
Converting 499 to CCCCLXXXXVIIII...
Converting 500 to D...
Converting 501 to DI...
Converting 1000 to M...
ok