• +43 660 1453541
  • contact@germaniumhq.com

Python Unit Testing


Python Unit Testing

Python has bundled into it a rather decent testing framework, that resembles a lot JUnit, named unittest. This is packaged in the core python, and it’s simple to use. While I’m a big fan of Behave, sometimes, especially if testing low-level APIs, it’s less verbose to just write a core python unit test. In this article we explore how to create our first test.

First we need a simple module, that holds our tests. A simple folder that has an init file into it qualifies as that. My personal approach is to keep the nested modules in sync with the project, so the project structure looks somewhat like this:

+ app_module/
  - __init__.py
  + sub_module/
  - ... other files
+ tests/
  - __init__.py
  + app_module/
    + sub_module/
    - __init__.py

Then the unit test for a file such as app_module/sub_module/some_functionality.py is in the corresponding path: tests/app_module/sub_module/test_some_functionality.py.

Here we see the first requirement. The test files that are scanned by the embedded unittest modules must start with test_.

Then in them we have a lot like JUnit. Here’s a sample from a State Machine unit test I wrote a while back:

import unittest
from smpy.XyzStateMachine import XyzStateMachine, XyzState, XyzStateChangeEvent

class TestXyzStateMachine(unittest.TestCase): # (1)
    # ...
    def test_initial_change_event(self): # (2)
        stateMachine = XyzStateMachine()

        def after_enter(ev: XyzStateChangeEvent):
            self.assertTrue(ev.previous_state is None) # (3)

        stateMachine.after_enter(XyzState.DEFAULT, after_enter)
        stateMachine.changeState(XyzState.RUNNING)
    # ...

if __name__ == '__main__':  # (4)
    unittest.main()
  1. The test case must inherit from unittest.TestCase

  2. Each actual test method from the TestCase object must also be prefixed by test_.

  3. Functions such as assertTrue, assertEqual, etc. are directly available on the self reference of the test case object. They show the actual difference on assertEqual, and also allow for custom error messages in the other assert* ones.

  4. Run each test as its own file when the file is executed as a python script.

In order to execute the tests, simply run in the top project folder:

python -m unittest

This spins the unittest runner, and runs everything. It instantiates the unittest.TestCase objects, and runts all the test_* methods, exactly like in JUnit. To run a single test we just execute it as a regular script, since we have wired the unittest execution when this happens:

python testing/smpy/test_XyzStateMachine.py

Happy testing!