1.SimpleTestCase, TestCase, and TransactionTestCase
The three classes differ in how they handle the database, and choosing wrongly is either slow or subtly broken. `SimpleTestCase` **forbids** database access — touching it raises, which is exactly what you want for a template filter or a pure function. `TestCase` wraps each test in a transaction and rolls it back afterwards, which makes it fast and is the right default. `TransactionTestCase` genuinely commits and then truncates the tables between tests, which is much slower but is the only one that can test anything involving real commits — `transaction.on_commit()` callbacks, `select_for_update()`, or code that expects another connection to see its writes.
Key features
- `SimpleTestCase` raises on any database query — use it for pure functions, template filters, form validation without a model, and URL resolution
- `TestCase` wraps each test in a transaction and rolls back: fast, isolated, and the right default for almost everything
- Because `TestCase` never commits, `transaction.on_commit()` callbacks do not run — use `captureOnCommitCallbacks()` to execute and inspect them
- `TransactionTestCase` commits for real and truncates tables afterwards; it is much slower and resets sequences, so hard-coded primary keys break
- Anything needing a real commit — `select_for_update()` against a second connection, database-level triggers, cross-connection visibility — needs `TransactionTestCase`
- `setUpTestData` runs once per class rather than per test, but the objects are shared: mutate one and re-fetch, or the change leaks into the next test
Which class, and what it costs
- TestCase — never commits
- Each test runs inside a transaction that is rolled back.
- Fast, and isolated without truncating anything.
- on_commit callbacks never fire — the assertion silently passes.
- A second connection cannot see the writes, so locking tests prove nothing.
- Right for almost every test that is not about committing.
- TransactionTestCase — really commits
- Writes are committed, then tables are truncated between tests.
- on_commit callbacks fire exactly as they do in production.
- A second connection sees the data, so select_for_update is testable.
- Sequences reset, so tests assuming a specific pk break.
- Noticeably slower — reserve it for tests about commits.
Command
class MyTests(TestCase):
def test_side_effect(self):
with self.captureOnCommitCallbacks(execute=True):
do_the_thing()`SimpleTestCase` forbids the database, which turns "this should be pure" into an enforced rule. `TestCase` wraps each test in a rolled-back transaction — fast, isolated, and the right default — but because it never commits, `on_commit` callbacks do not fire and other connections cannot see your writes, so use `captureOnCommitCallbacks(execute=True)` rather than switching classes. `TransactionTestCase` really commits and truncates, and is only worth its cost when the commit itself is what you are testing.