El patrón de diseño Mother lo vi inicialmente en este post de Martin FowlerSe abre en una nueva pestaña. Lo uso para gestionar datos de prueba (fixtures) e incluso para mockear pantallas de forma coherente, predecible y reutilizable. Aquí tienes una guía práctica y simplificada para que lo apliques paso a paso.
¿Qué problema resuelve?
- Evita duplicar estructuras de datos complejas.
- Hace explícitas variantes habituales (por ejemplo "usuario válido", "usuario con email inválido", etc).
- Permite sobreescribir (overrides) valores para adaptar solo lo necesario en cada test.
¿Cómo aplicar el patrón mother?
Supongamos que tenemos un usuario:
interface User { id: number email: string }
Necesitamos tener en código una serie de datos de prueba. Para ello creamos un fichero user.mother.ts
export class UserMother { static base(): User { return { id: 1, email: 'user@example.com', } } }
Suelo usar para nombres de ficheros kebab-case y añado como sufijo la tipología.
En mis proyectos verás nombres como get-user.qry.ts o dashboard.page.tsx
Uso métodos estáticos para evitar tener que crear instancias. Para usar en un test sería así: UserMother.base().
describe('user validator', () => { it('should validate an user', () => { const user = UserMother.base() const isValid = userValidator.validate(user) expect(isValid).toBe(true) }) })
De esta forma no tendremos que repetir una y otra vez los mismos datos de prueba.
Nombres con intención
Cuando creamos los mothers, me parece interesante darles nombres. Además, creo una historia acerca de la persona que quiero representar. Os presento a John y Jane. John es alguien que no es muy versado en la informática, es algo además despistado y, por tanto, suele tener errores en los datos que introduce.
Por otro lado, tenemos a Jane, experta en informática. Sus datos siempre están bien formados.
Con estas premisas vamos a mejorar el nombre .base e introducir estos usuarios.
export class UserMother { // Jane: persona experta, datos bien formados static jane(): User { return { id: 101, email: 'jane.doe@company.com', } } // John: persona principiante, datos mal formados static john(): User { return { id: 102, email: 'john..doe@@example', // email inválido } } }
Overrides controlados con Partial<T>
¿Qué ocurre si necesita cambiar una propiedad del objeto para un test en concreto? Para eso tenemos los overrides:
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, }) } }
Todas las factorías aceptan override?: Partial<User>. Esto permite ajustar los campos sin perder la forma del objeto.
const user = UserMother.jane({ id: 999 })
Esta técnica minimiza ruido, mantiene consistencia y reduce el mantenimiento de tests.
Consejos prácticos
- Mantén las variantes más usadas y elimina las que no aportan casos reales.
- Prefiere
overrideen lugar de crear una variante por cada combinación posible. - Extrae conversiones/serializaciones a funciones claras y testeables por separado.
- Usa valores que parezcan reales (fechas coherentes, duraciones consistentes) para detectar errores con mayor facilidad.
Conclusión
El patrón mother permite crear conjuntos de datos para que los tests sean más mantenibles. Añadiendo nombres con intención y permitiendo hacer uso de overrides proveerás de mucho valor a la hora de hacer los tests.