skip to content

setUp, tearDown and Cleanup Hooks

How unittest builds and tears down state around each test, class and module, and why addCleanup survives a setUp that dies halfway. Interviewers probe whether your tests leak state into each other.

part ofPythonoverview, primer and where to startread it →
on this pageshow

questions

3

What do setUp and tearDown do in a unittest.TestCase, and when does each run?

level: juniorimportance: must knowfreq 72%

answer

  1. Tests must not share their state
  2. Something wraps every test method
  3. A new object built per test method
  4. One hook is skipped when setup dies

basics

~10 s

unittest calls setUp before every test_ method and tearDown after it, on a brand-new TestCase instance built per method. setUp prepares that test's state; tearDown releases it. If setUp raises, tearDown is skipped.

solid answer

~40 s

unittest instantiates the `TestCase` subclass **once per test method**, so each test gets its own object. For each one the order is `setUp()`, the `test_*` method, `tearDown()`, then any functions registered with `addCleanup()` in reverse order. `setUp` builds the per-test state and `tearDown` undoes it. Two asymmetries matter. If the test method fails or raises, `tearDown` still runs - that is the point of it. But if `setUp` itself raises, the test method is skipped, the result is an **error** rather than a failure, and `tearDown` is **not** called, so anything `setUp` acquired before dying leaks. The fresh instance protects only attributes on `self`; class attributes, module globals, environment variables and monkey-patched modules are shared, and undoing those is what teardown is for.

code

python · 19 lines
python
import unittest


class FreshInstanceTests(unittest.TestCase):
    def setUp(self):
        self.samples = []

    def tearDown(self):
        print("tearDown", self.samples)

    def test_appends(self):
        self.samples.append(1)
        self.assertEqual(self.samples, [1])

    def test_starts_empty(self):
        self.assertEqual(self.samples, [])


unittest.main(verbosity=0, exit=False)

go deeper

for a junior

Be ready to state the order out loud: setUp, the test method, tearDown, once for every test_ method. Remember the camelCase spelling and that each test gets its own instance, so attributes on self never leak.

for a middle

Explain the mechanics: the loader builds one instance per test method, tearDown is skipped when setUp raises, and a failure differs from an error. Show why addCleanup is the safer default for anything acquired in setUp.

for a senior

Demonstrate that you know isolation stops at the instance. Talk about the process-level state - env vars, monkey patches, registries, logging config - that tests actually leak through, and how you make teardown unconditional rather than hopeful.

for a principal

Own the policy: what a suite is allowed to share, whether fixture scope may ever widen for speed, and how you stop order-dependent tests from becoming normal. A suite whose results depend on run order is a trust problem, not a performance one.

### The contract A `unittest.TestCase` subclass is not a bag of loose functions. For every method whose name starts with `test`, the loader builds a **separate instance** of the class, passing that method name to the constructor, and collects the instances into a suite. Running one instance executes a fixed sequence: 1. `setUp()` 2. the `test_*` method 3. `tearDown()` 4. every function registered with `addCleanup()`, in reverse registration order `setUp` is where you build the state one test needs; `tearDown` is where you undo it. ### One instance per test method - and what that does *not* cover Because each test method runs on its own object, anything assigned to `self` is private to that test. If `setUp` does `self.samples = []`, a test that appends to it cannot reach any other test. That is the whole isolation guarantee, and it is narrower than most candidates assume: - **class attributes are shared** - anything `setUpClass` parks on `cls`, or a test sets via `type(self).cache = ...`, survives into the next test; - **module globals are shared** - an import-time registry, a cached connection, a module-level list; - **process state is shared** - environment variables, the working directory, `sys.path`, signal handlers, logging configuration, and any attribute you monkey-patched on another module. `setUp` gives you a clean *instance*, never a clean *process*. Undoing process-level changes is exactly what `tearDown` and the cleanup stack are for. ### The failure paths - the part interviewers actually probe - If **`setUp` raises**, the test method does not run and **`tearDown` does not run either**. unittest reports the test as an **error**. Only the cleanups already registered at the moment of the exception are executed. So a `setUp` that acquires two resources and dies between them leaks the first one, unless each acquisition is immediately followed by `self.addCleanup(...)` - or `self.enterContext(...)`, added in 3.11. - If the **test method** fails an assertion or raises anything else, `tearDown` **is** called. Teardown is not conditional on success; that is its entire reason to exist. - If **`tearDown` itself raises**, that is recorded as an additional error against the test, and the test's original outcome is still reported. - unittest distinguishes a **failure** (an assertion method raised `AssertionError`) from an **error** (any other exception, including one out of `setUp`). Both are red; only the classification differs, and the distinction is what tells you whether the fixture or the behaviour broke. ### The wider scopes Two coarser scopes exist. `setUpClass` and `tearDownClass` are class methods run once around all the tests in a class. A test module may define module-level `setUpModule()` and `tearDownModule()` functions, run once around every class in that module. Both exist to amortize genuinely expensive setup, and both buy speed by surrendering per-test isolation: whatever they build lives on the class or in the module for the rest of the run. If `setUpClass` raises, that class's tests are errored and `tearDownClass` is skipped - though cleanups registered with `addClassCleanup` still run. ### Practical rules - Prefer `self.addCleanup(fn, *args)` over `tearDown`. It runs even when `setUp` dies partway through, it pairs each acquisition with its release at the point of acquisition, and it survives inheritance without anyone remembering `super()`. - If you do write `tearDown`, call `super().setUp()` first and `super().tearDown()` last in any `TestCase` hierarchy. Forgetting silently drops the base class's fixture. - Do not assert in `setUp`. An assertion there errors every test in the class and hides which behaviour actually regressed. - The spelling matters. `setUp` and `tearDown` are camelCase; a method named `setup` is an ordinary method that unittest never calls, and the suite goes green for the wrong reason. - Do not depend on execution order. Within a class, methods run in sorted-name order by default, but that is a loader detail rather than a promise - and a test that needs a particular order is the classic symptom of leaked state. - Since 3.11, returning a non-`None` value from a test method emits a `DeprecationWarning`. In practice it catches `return self.assertEqual(...)` and coroutines that were never awaited. ### What good looks like Cheap per-test state built in `setUp`; every acquisition paired immediately with a cleanup; nothing mutable parked on the class unless a measured cost forces it there. When a test only passes if another test ran first, the fixture - not the code under test - is what is broken.

  • Does tearDown run if the test method raises an exception?
    Yes. `tearDown` runs whenever the test method actually started, regardless of whether it passed, failed an assertion, or raised something else. It is skipped only when `setUp` itself raised, because then the test method never ran. If `tearDown` raises in turn, unittest records that as an extra error against the test and still reports the original result.
  • What is the difference between a failure and an error in unittest's output?
    A **failure** means an assertion method raised `AssertionError` - the code under test produced the wrong answer. An **error** means any other exception escaped, including one raised inside `setUp`. The distinction is diagnostic: errors usually point at a broken fixture, an import problem or an unexpected exception type, while failures point at the behaviour you were actually asserting on.
  • If a test sets an attribute on the class rather than on self, does the next test see it?
    Yes. The per-test isolation covers only the instance. `self.x = 1` dies with that instance, but `type(self).x = 1` writes a class attribute that every subsequent test in the class reads, and module globals and process state leak even wider. That asymmetry is why teardown or a registered cleanup is required for anything set outside the instance.

Each test method gets its own clean workbench, but every test shares the same room - so tools you left on the bench vanish, and a window you opened stays open.

saying these in an interview costs you the question

  • Says unittest reuses one TestCase instance for every test
  • Thinks tearDown still runs after setUp raised
  • Believes setUp gives a clean process, not just a clean instance
  • Puts assertions in setUp instead of the test method
  • Names the hook setup and wonders why it never runs
  • Assumes tearDown is skipped when the test fails

context

open as a page

Why does self.addCleanup still run when a unittest setUp raises halfway through?

level: middleimportance: should knowfreq 46%

basics

~10 s

addCleanup registers a callable the moment you call it, and unittest drains that stack after every test - even one whose setUp died partway. tearDown, by contrast, is skipped entirely when setUp raises.

open as a page

Your unittest setUp rebuilds a metrics-scraper fixture with a 45-second cold start per test - when is setUpClass the right fix?

level: seniorimportance: should knowfreq 40%

basics

~20 s

setUpClass runs once per class instead of once per test, removing the repeated cold start. Take it when the shared object is effectively read-only; if it holds buffers, counters or registries, sharing costs you test isolation.

open as a page