There are many ways to write unit tests in Python.

unittest

Here the focus is living off the land with built-in unittest.

unittest is both a framework and test runner, meaning it can execute your tests and return the results. In order to write unittest tests, you must:

  1. Write your tests as methods within classes
  2. These TestCase classes must subclass unittest.TestCase
  3. Names of test functions must begin with test_
  4. Import the code to be tested
  5. Use a series of built-in assertion methods

Basic example

import unittest

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)

if __name__ == '__main__':
    unittest.main()

Assertions

The TestCase class provides several assert methods to check for and report failures.

Assertion Checks
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)

Exceptions

Validates an exception is raised when callable is called with any positional or keyword arguments that are also passed to assertRaises().

class TestComputedAttrs(unittest.TestCase):
    def setUp(self) -> None:
        self._sut = ParrotComputedAttributes()

    def test_read_missing_property(self):
        with self.assertRaises(AttributeError):
            self._sut.missing_property

Running

A common way is to trigger unittest.main() like so:

if __name__ == "__main__":
    unittest.main()

Alternatively it provides a first-class CLI, that supports simple module, class or individual test methods:

python3 -m unittest objects.test.test_parrot.MyTestCase.test_default_voltage

Test modules can also be specified by path:

python3 -m unittest objects/test/test_parrot.py

Pro tips

  • If you shuffle tests into a test directory in the source tree, you’ll need to make sure the code under test is available on the PYTHONPATH

pytest

Pytest is the defacto standard.

TODO

  • unittest
  • monkey patching
  • pytest
  • mocking