Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Fixture

Pattern

A reusable solution you can apply to your work.

Also known as: Test Fixture, Test Data

Context

A Test needs to run in a known state. The function under test might need a database with specific records, a file system with specific files, or an object configured in a specific way. The fixture is that known starting point. This is a tactical pattern that works closely with the Harness to make tests reliable and repeatable.

Problem

Tests that depend on external state are fragile. If a test expects a specific user to exist in the database and someone deletes that user, the test fails for reasons unrelated to the code it’s checking. If two tests share state and one modifies it, the other may pass or fail depending on execution order. How do you give each test a clean, predictable starting point?

Forces

  • Tests need data and environment to run against.
  • Shared state between tests creates hidden dependencies and flaky results.
  • Setting up realistic state can be slow and complex.
  • Overly simplified fixtures may miss real-world bugs.
  • Fixture code must be maintained alongside the code it tests.

Solution

Create a fixed, controlled setup for each test or group of tests. A fixture provides the data, objects, configuration, and environment that the test needs, and nothing more. After the test runs, the fixture is torn down so the next test starts fresh.

Fixtures can be as simple as a few variables or as complex as a populated database. Common approaches:

Inline fixtures declare their data directly in the test. This is the clearest approach for simple tests; you can see everything the test needs by reading the test itself.

Shared fixtures are set up once and reused across multiple tests. This saves time but introduces the risk of one test contaminating another. Most harnesses offer “setup before each test” and “setup once before all tests” hooks to manage this tradeoff.

Factory fixtures use helper functions or libraries to generate test data with sensible defaults. Instead of specifying every field of a user record, you call make_user(name="Alice") and the factory fills in the rest. This keeps tests focused on what matters.

External fixtures load data from files (JSON snapshots, SQL dumps, recorded API responses). These are useful for complex data structures but can become stale if the data format changes.

How It Plays Out

An e-commerce test suite needs order data. Each test that involves orders uses a factory: create_order(items=3, status="shipped"). The factory generates a complete order with realistic but deterministic data. Tests are readable (you see the relevant setup at a glance) and isolated, because each test creates its own order.

In an agentic workflow, fixtures serve a dual purpose. They provide the test data that lets an AI agent verify its work, and they document the expected shape of the system’s data. When an agent sees a fixture that creates a user with an email, a name, and a role, it learns the structure of a user without reading the schema. Well-named fixtures become a form of living documentation.

Warning

Beware of fixture bloat. If setting up a test requires 50 lines of fixture code, the test is probably testing too many things at once, or the code under test has too many dependencies. Fixture pain is a design signal.

Example Prompt

“Create a test factory for Order objects. It should accept optional overrides for status, item count, and customer ID, and fill in sensible defaults for everything else. Use it in all the order-related tests.”

Consequences

Good fixtures make tests fast, reliable, and readable. Each test starts from a known state, runs its checks, and cleans up. Failures point to real bugs, not to stale data or test ordering issues.

The cost is maintenance. Fixtures are code, and they must evolve alongside the system. When a data model changes (a new required field, a renamed column) every fixture that touches that model must be updated. Factory-based fixtures reduce this cost by centralizing the construction logic in one place.

  • Used by: Test — tests consume fixtures.
  • Managed by: Harness — the harness handles fixture lifecycle.
  • Refines: Test Oracle — fixtures provide the controlled inputs that make oracle comparisons deterministic.
  • Contrasts with: Observability — fixtures control inputs for testing, while observability captures outputs in production.