Setting up and tearing down a test harness

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.

How to do it...

With the following steps, we will setup and teardown a test harness for each test method.

  1. Create a new file called recipe2.py in which to put all our code for this recipe.
  2. Pick a class to test. In this case, we will use a slightly altered version of our Roman numeral converter, where the function, not the constructor, provides the input value to convert.
    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
  3. Create a test class using the same name as the class under test with Test appended to the end.
    import unittest
    class RomanNumeralConverterTest(unittest.TestCase):
  4. Create a setUp method that creates an instance of the class under test.
      def setUp(self):
        print "Creating a new RomanNumeralConverter..."
        self.cvt = RomanNumeralConverter()
  5. Create a tearDown method that destroys the instance of the class under test.
      def tearDown(self):
        print "Destroying the RomanNumeralConverter..."
        self.cvt = None
  6. Create all the test methods using 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)
  7. Make the entire script runnable and then use the test runner of unittest.
    if __name__ == "__main__":
      unittest.main()
  8. Run the file from the command line.
    How to do it...

How it works...

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.

Tip

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.198.59