In this post, we will be applying the commands from the previous post to the stack example. High code coverage is especially useful for interpreted languages such as Python, to guarantee that at least function calls do not include typos.

chemistry-23400_640   Experiment:

  1. Download the stack example and save it as stack.py
  2. Create the file test_stack_unittest.py with the following contents
    import unittest
    from stack import *
    
    class TestStack(unittest.TestCase):
    
      def setUp(self):
        pass
    
      def testInit(self):
        s = Stack()
        self.assertTrue(s.is_empty())
    
    if __name__ == '__main__':
      unittest.main()
    
    
  3. Run (together with code coverage)
    coverage run test_stack_unittest.py
    

    The output should look as follows:

    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.000s
    
    OK
    

    That is, the one test implemented succeeded

  4. Check the coverage report
    coverage report -m
    

    Output (notice the information about the missing lines in the rightmost column):

    Name                     Stmts   Miss  Cover   Missing
    ------------------------------------------------------
    stack.py                    16      8    50%   11-16, 19-23
    test_stack_unittest.py      10      0   100%
    ------------------------------------------------------
    TOTAL                       26      8    69%
    
  5. Since lines 11-16 were not covered, we now try to build a test for the implementation of push.
    We add the following test to test_stack_unittest.py:

    def testPush(self):
      s = Stack()
      s.push(3)
      popped = s.pop()
      self.assertEqual(popped, 3)
    

    And re-run tests with code coverage analysis:

    coverage run test_stack_unittest.py
    coverage report -m
    
  6. The output is:
    ..
    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s
    
    OK
    

    And the coverage report:

    Name                     Stmts   Miss  Cover   Missing
    ------------------------------------------------------
    stack.py                    16      2    88%   12, 20
    test_stack_unittest.py      15      0   100%
    ------------------------------------------------------
    TOTAL                       31      2    94%
    

    That is, line 11 of stack.py is now covered, but line 12 isn’t.

  7. Observing the code, we notice that this statement is executed when we have pushed an element which was then popped and we already have the space in the stack to add the element. Let’s check that the value is indeed updated with the following test:
    def testPushAfterPop(self):
      s = Stack()
      s.push(3)
      s.pop()
      s.push(5)
      popped = s.pop()
      self.assertEqual(popped, 5)
    

    With this new test the output is:

    ...
    ----------------------------------------------------------------------
    Ran 3 tests in 0.000s
    
    OK
    
    Name                     Stmts   Miss  Cover   Missing
    ------------------------------------------------------
    stack.py                    16      1    94%   20
    test_stack_unittest.py      22      0   100%
    ------------------------------------------------------
    TOTAL                       38      1    97%
    
  8. Line 20 of stack.py is when we try to pop an element from an empty stack. We create a test case for this situation:
    def testPopFromEmpty(self):
      s = Stack()
      popped = s.pop()
      self.assertFalse(popped)
    

    (The last line expresses “not popped”, i.e. popped is None.
    With this new test the output is:

    ....
    ----------------------------------------------------------------------
    Ran 4 tests in 0.001s
    
    OK
    
    Name                     Stmts   Miss  Cover   Missing
    ------------------------------------------------------
    stack.py                    16      0   100%
    test_stack_unittest.py      26      0   100%
    ------------------------------------------------------
    TOTAL                       42      0   100%
    

We now have a test suite with 100% statement coverage (every statement has been executed at least once). It was very easy to implement and run for this stack example.

student-41444_640.png What other things can I do with this example?

  • Check what happens when run with branch coverage (for every conditional statement (such as if) there must be at least one execution of the condition being true and another one of the condition being false)
    coverage run --branch test_stack_unittest.py
    coverage report -m
    
  • Apply these ideas to your examples
Advertisements