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.


