A test that spells out every field of an object is noisy and breaks whenever the object gains a field. A builder supplies valid defaults and lets each test override only the fields it exercises, so the test states its intent and survives unrelated changes.
Before you start
You should be comfortable with test structure and objects. This article covers builders and their benefits.
Step-by-step walkthrough
Step 1: Provide a valid default
A builder function returns an object with sensible values for every required field, so the test starts from something valid. The test then fails only for the reason it is testing, not because a required field was missing.
Step 2: Override per test
Pass an overrides object merged over the defaults, so each test sets only the fields relevant to it. A test about inactive users sets status: 'inactive' and leaves the rest, which makes it obvious what the scenario is.
Step 3: Return fresh objects
Build a new object each call rather than sharing a module-level fixture, so one test cannot mutate the data another test uses. This removes an entire class of order-dependent failures.
Worked scenario
The builder defaults and the test overrides one field.
function makeUser(overrides = {}) {
return { id: 'u-1', name: 'Ada', status: 'active', ...overrides };
}
test('inactive user cannot log in', () => {
const user = makeUser({ status: 'inactive' });
expect(canLogin(user)).toBe(false);
});Walk through the example
makeUser returns a complete, valid user, so the test does not need to specify id or name. The override sets only status, which is the scenario under test. Each call produces a fresh object, so tests are independent.
Common mistake
Sharing a mutable fixture object across tests, so one test’s change leaks into another, or repeating every field in every test, which makes tests long and brittle to schema changes.
Verify the behavior
Add a required field to the domain object and confirm tests using the builder still pass with a default added in one place. Run tests in random order and confirm no failures from shared state. Confirm each test sets up only the fields it asserts on.
Interview exercise
Why is a builder better than a shared fixture object?
Answer and reasoning
A builder produces a fresh valid object per test and lets each test override only relevant fields, so tests are independent and readable. A shared fixture is a single mutable object whose state can leak between tests, causing order-dependent failures, and it hides what each test actually depends on.
Continue learning
Compare fixture isolation in Fixture isolation and test structure in Arrange, act, assert. Read the Martin Fowler on object mothers and try the Testing interview questions.