setUp, tearDown and cleanups

unittest · Practical · 11 min

Fixtures remove duplicated setup and guarantee that temp files, servers and patches are cleaned up.

Fixtures prepare the world a test needs and tear it down afterwards.

  • setUp(self) runs before each test method; tearDown(self) after each, even if the test failed (but not if setUp itself raised).
  • setUpClass(cls) / tearDownClass(cls), decorated with @classmethod, run once per class. Use them for expensive, read-only resources such as a database schema. Shared mutable state between tests causes order-dependent failures.
  • setUpModule() / tearDownModule() are functions at module level for once-per-file setup.

Cleanups are more robust than tearDown. self.addCleanup(func, *args) registers a function to call after the test; cleanups run in reverse order (LIFO) and run even if setUp failed after registering them. Register each cleanup right after acquiring the resource.

self.enterContext(cm) (3.11+) enters a context manager and registers its exit as a cleanup, returning the __enter__ result. That works for temp directories, open files and mock.patch:

self.tmp = self.enterContext(tempfile.TemporaryDirectory())

addClassCleanup / enterClassContext are the class-level equivalents.

Example

import tempfile, unittest
from pathlib import Path

class TestNotes(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print("setUpClass once")

    def setUp(self):
        self.dir = Path(self.enterContext(tempfile.TemporaryDirectory()))
        self.file = self.dir / "notes.txt"
        self.file.write_text("hello")
        self.addCleanup(print, "cleanup for", self._testMethodName)

    def test_read(self):
        self.assertEqual(self.file.read_text(), "hello")

    def test_append(self):
        with self.file.open("a") as f:
            f.write("!")
        self.assertEqual(self.file.read_text(), "hello!")

unittest.main(exit=False)
# setUpClass once / cleanup for test_append / cleanup for test_read
# Ran 2 tests ... OK

Remember

  • setUp/tearDown run around every test; setUpClass once per class
  • addCleanup runs LIFO, even after failures in setUp
  • enterContext(cm) enters a context manager and schedules its exit

Watch out for

  • Mutating objects created in setUpClass, so tests pass or fail depending on order.

Check yourself

  1. setUp raises an exception halfway through. What still runs?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/unittest.html#unittest.TestCase.addCleanup