LogoCésar Alberca
December 19, 2025 - 10 minutes

The Mother Design Pattern

Design Better Frontend Architecture
Complex Frontend Architectural Patterns made easy. Timely and directly to your inbox. Become a better Frontend Architect.
I respect your privacy. Unsubscribe at any time.

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 override instead 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.

César Alberca