I first came across the Mother design pattern in this post by Martin FowlerOpen in a new tab. I use it to manage test data (fixtures) and even to mock screens in a coherent, predictable, and reusable way. Here's a practical, simplified guide so you can apply it step by step.
#What problem does it solve?
- Avoids duplicating complex data structures.
- Makes common variants explicit (for example, "valid user", "user with invalid email", etc.).
- Allows overrides to adapt only what's needed in each test.
#How to apply the mother design pattern?
Let's suppose we have a user:
interface User {
id: number
email: string
}
We need a set of test data in code. For that, we create a user.mother.ts file:
export class UserMother {
static base(): User {
return {
id: 1,
email: 'user@example.com',
}
}
}
I usually use kebab-case for file names and add the type as a suffix.
In my projects you'll see names like get-user.qry.ts or dashboard.page.tsx.
I use static methods to avoid having to create instances. In a test you would use it like this: UserMother.base().
describe('user validator', () => {
it('should validate a user', () => {
const user = UserMother.base()
const isValid = userValidator.validate(user)
expect(isValid).toBe(true)
})
})
This way, we won't have to repeat the same test data over and over again.
#Intention-revealing names
When creating mothers, I find it useful to give them names. I also like to craft a small story about the person I want to represent. Meet John and Jane. John is not very tech-savvy and a bit absent-minded, so he often makes mistakes in the data he enters.
On the other hand, we have Jane, who is experienced with computer. Her data is always well-formed.
With these premises, let's improve the .base name and introduce these users.
export class UserMother {
// Jane: expert person, well-formed data
static jane(): User {
return {
id: 101,
email: 'jane.doe@company.com',
}
}
// John: beginner person, malformed data
static john(): User {
return {
id: 102,
email: 'john..doe@@example', // invalid email
}
}
}
#Controlled overrides with Partial<T>
What if you need to change one property of the object for a specific test? That's what overrides are for:
export class UserMother {
static base(override?: Partial<User>): User {
const base: User = {
id: 1,
email: 'user@example.com',
}
return { ...base, ...override }
}
static jane(override?: Partial<User>): User {
return this.base({
id: 101,
email: 'jane.doe@company.com',
...override,
})
}
}
All factories accept override?: Partial<User>. This lets you tweak fields without losing the object's shape.
const user = UserMother.jane({ id: 999 })
This technique minimizes noise, maintains consistency, and reduces test maintenance.
#Practical tips
- Keep the most commonly used variants and remove those that don't reflect real cases.
- Prefer
overrideinstead of creating a variant for every possible combination. - Extract conversions/serializations into clear, separately testable functions.
- Use values that look real (coherent dates, consistent durations) to catch errors more easily.
#Conclusion
The mother pattern helps you create data sets that make tests more maintainable. By adding intention-revealing names and enabling overrides, you'll add a lot of value when writing tests.


