Card 05/ 09

ExampleDifficulty: Intermediate1 min

Template Method in the Wild: Python's unittest.TestCase

The runner calls setUp, your test, then tearDown, in that order, for every test.

Python's unittest.TestCase is a template method you have probably already used. You subclass it and write setUp, tearDown and methods whose names begin with test, and you never write the code that calls them. That code is TestCase.run, the template method, and this card prints each call to show the order it enforces.

py
import inspect
import io
import sys
import unittest


class BasketTest(unittest.TestCase):
    def setUp(self) -> None:
        print("setUp")
        self.basket: list[int] = []

    def test_empty(self) -> None:
        print("  test_empty")
        self.assertEqual(sum(self.basket), 0)

    def test_add(self) -> None:
        print("  test_add")
        self.basket.append(500)
        self.assertEqual(sum(self.basket), 500)

    def tearDown(self) -> None:
        print("tearDown")


suite = unittest.defaultTestLoader.loadTestsFromTestCase(BasketTest)
unittest.TextTestRunner(stream=io.StringIO()).run(suite)

source = inspect.getsource(unittest.TestCase.run)
print("Python", sys.version.split()[0])
print("run calls setUp, the test and tearDown:", all(
    name in source for name in ("_callSetUp", "_callTestMethod", "_callTearDown")))
OutputRan with python:3.12-slim (Python 3.12.13)
setUp
  test_add
tearDown
setUp
  test_empty
tearDown
Python 3.12.13
run calls setUp, the test and tearDown: True
Each test is wrapped in its own setUp and tearDown.

Each test runs between a fresh setUp and a tearDown, so test_add's item does not reach test_empty. The tests run in alphabetical order of their names, which is the default of the test loader, not the order they appear in the class. The last line checks the source of TestCase.run in Python 3.12.13, and confirms that it calls the internal methods that run setUp, the test and tearDown.

The parts map onto the template. TestCase is the abstract class and run is the template method. setUp and tearDown are hooks, with defaults that do nothing, and each test_ method is the step a subclass supplies. BasketTest is the concrete class.

This is the Hollywood principle at work: the framework calls you. Many frameworks are built this way, which is why learning to find the template method is often the quickest way to learn when your code will run.