Unittest provides an easy mechanism to configure the state of the system when a piece of code is put through a test. It also allows us to clean things up afterwards, if necessary. This is commonly needed when a particular test case has repetitive steps used in every test method.
Barring any references to external variables or resources that carry state from one test method to the next, each test method starts from the same state.
With the following steps, we will setup and teardown a test harness for each test method.
recipe2.py
in which to put all our code for this recipe.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] return val
Test
appended to the end.import unittest class RomanNumeralConverterTest(unittest.TestCase):
setUp
method that creates an instance of the class under test.def setUp(self): print "Creating a new RomanNumeralConverter..." self.cvt = RomanNumeralConverter()
tearDown
method that destroys the instance of the class under test.def tearDown(self): print "Destroying the RomanNumeralConverter..." self.cvt = None
self.converter
.def test_parsing_millenia(self): self.assertEquals(1000, self.cvt.convert_to_decimal("M")) def test_parsing_century(self): self.assertEquals(100, self.cvt.convert_to_decimal("C")) def test_parsing_half_century(self): self.assertEquals(50, self.cvt.convert_to_decimal("L")) def test_parsing_decade(self): self.assertEquals(10, self.cvt.convert_to_decimal("X")) def test_parsing_half_decade(self): self.assertEquals(5, self.cvt.convert_to_decimal("V")) def test_parsing_one(self): self.assertEquals(1, self.cvt.convert_to_decimal("I")) def test_empty_roman_numeral(self): self.assertTrue(self.cvt.convert_to_decimal("") == 0) self.assertFalse(self.cvt.convert_to_decimal("") > 0) def test_no_roman_numeral(self): self.assertRaises(TypeError, self.cvt.convert_to_decimal, None)
if __name__ == "__main__": unittest.main()
In the first step, we picked a class to test. Next, we created a separate test class. By naming the test class [class under test]Test
, it is easy to tell which class is under test.
Then, we defined a setUp
method that unittest runs before every test
method. Next, we created a tearDown
method that unittest runs after every test
method. In this case, we added a print statement in each of them to demonstrate unittest re-running these two methods for every test method. In reality, it would probably add too much noise to our testing.
One deficiency of unittest is the lack of setUpClass/tearDownClass
and setUpModule/tearDownModule
, providing the opportunity to run code in greater scopes than at the test method level. This has been added to unittest2, and has been described by some as handy, but won't be covered within the scope of this book.
Each test case can have one setUp and one tearDown method
Our RomanNumeralConverter
is pretty simple and fits easily into a single test class. But the test class allows only one setUp
method and one tearDown
method. If different combinations of setUp
/tearDown
methods are needed for various test scenarios, then this is a cue to code more test classes.
Just because we write a setUp
method doesn't mean we need a tearDown
method. In our case, we could have skipped destroying the RomanNumeralConverter
, because a new instance would be replacing it for every test method. It was really for demonstration purposes only. What are the other uses of those cases which need a tearDown
method? Using a library that requires some sort of close
operation is a prime candidate for writing a tearDown
method.
18.119.159.178