Mocks y Testing
Genera datos de prueba realistas para tests unitarios, Storybook, y prototipos con la API mock() de QuickModel.
Por qué usar mock()
QuickModel genera mocks que respetan tus transformaciones: si el modelo tiene createdAt: Date, el mock devuelve una instancia Date real, no un string.
API Básica
typescript
import { QModel, Quick } from 'quickmodel';
interface IUser {
id: string;
name: string;
email: string;
age: number;
createdAt: string;
tags: string[];
isActive: boolean;
}
@Quick({ createdAt: Date, tags: Set })
class User extends QModel<IUser> {
declare id: string;
declare name: string;
declare email: string;
declare age: number;
declare createdAt: Date;
declare tags: Set<string>;
declare isActive: boolean;
}
// Instancia única con valores aleatorios realistas
const user = User.mock().random();
console.log(user instanceof User); // true
console.log(user.createdAt instanceof Date); // true ✅ respeta las transformaciones
console.log(user.tags instanceof Set); // true ✅
// Instancia con datos vacíos/por defecto
const emptyUser = User.mock().empty();
console.log(emptyUser.name); // '' (string vacío)
console.log(emptyUser.age); // 0
// Instancia con valores de muestra predecibles
const sampleUser = User.mock().sample();
// Siempre devuelve los mismos datos (útil para snapshots)
// Instancia con todos los campos (incluyendo opcionales)
const fullUser = User.mock().full();
// Instancia solo con los campos requeridos
const minimalUser = User.mock().minimal();Arrays de Mocks
typescript
// 5 usuarios aleatorios
const users = User.mock().array(5);
console.log(users.length); // 5
console.log(users[0] instanceof User); // true
// Array con tipo específico
const samples = User.mock().array(3, 'sample'); // valores predecibles
const empties = User.mock().array(2, 'empty'); // valores vacíos
// Array con overrides por índice
const namedUsers = User.mock().array(3, 'random', (idx) => ({
name: `Usuario ${idx + 1}`,
email: `user${idx + 1}@test.com`,
}));
console.log(namedUsers[0].name); // 'Usuario 1'
console.log(namedUsers[2].name); // 'Usuario 3'Overrides: Sobreescribir Campos Específicos
typescript
// Solo sobreescribir los campos que nos interesan
const adminUser = User.mock().random({ isActive: true, tags: [] });
console.log(adminUser.isActive); // true (sobreescrito)
console.log(adminUser.name); // nombre aleatorio generado
// Mock vacío con valores específicos
const testUser = User.mock().empty({
name: 'Test User',
email: 'test@example.com',
});
console.log(testUser.name); // 'Test User'
console.log(testUser.age); // 0 (vacío por defecto)Objetos Planos (sin instancia de modelo)
A veces necesitas el objeto plano de la interfaz, no la instancia del modelo:
typescript
// Objeto de la interfaz (no es instancia de User)
const userData = User.mock().interfaceRandom();
// userData.createdAt es string (formato interfaz), no Date
// Array de objetos planos
const usersData = User.mock().interfaceArray(5);
// Útil para preparar fixtures de API:
const apiFixture = User.mock().interfaceRandom({
email: 'fixture@test.com',
});
// Pásalo directamente a tu función que mockea fetch()Tests Unitarios con Vitest / Jest
typescript
import { describe, it, expect } from 'vitest';
import { QModel, Quick, QRule } from 'quickmodel';
interface IOrder {
id: string;
total: number;
status: 'pending' | 'paid' | 'cancelled';
createdAt: string;
}
@Quick({ createdAt: Date })
class Order extends QModel<IOrder> {
declare id: string;
@QRule({
predicate: (val: number) => val > 0,
message: 'El total debe ser positivo',
})
declare total: number;
declare status: 'pending' | 'paid' | 'cancelled';
declare createdAt: Date;
}
describe('Order', () => {
it('transforma createdAt a Date', () => {
const order = Order.mock().random();
expect(order.createdAt).toBeInstanceOf(Date);
});
it('falla la validación si total es 0', () => {
const order = Order.mock().random({ total: 0 });
const result = order.$qCheckRules();
expect(result.valid).toBe(false);
expect(result.errors[0].field).toBe('total');
});
it('pasa la validación con datos correctos', () => {
const order = Order.mock().random({ total: 99.99, status: 'paid' });
expect(order.$qIsValid()).toBe(true);
});
it('serializa correctamente', () => {
const order = Order.mock().sample({ total: 50 });
const plain = order.$qSerialize();
expect(typeof plain.createdAt).toBe('string');
expect(plain.total).toBe(50);
});
});Storybook: Generar Props de Ejemplo
typescript
// UserCard.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { UserCard } from './UserCard';
import { User } from '../models/user.model';
const meta: Meta<typeof UserCard> = {
component: UserCard,
};
export default meta;
type IStory = StoryObj<typeof UserCard>;
export const Default: IStory = {
args: {
user: User.mock().random(),
},
};
export const InactiveUser: IStory = {
args: {
user: User.mock().random({ isActive: false }),
},
};
export const NewUser: IStory = {
args: {
// Usuario recién creado (hoy)
user: User.mock().random({
createdAt: new Date().toISOString(),
tags: [],
}),
},
};
export const MultipleUsers: IStory = {
args: {
users: User.mock().array(3, 'random'),
},
};Fixtures para E2E Tests
typescript
// fixtures/users.ts
export const USERS = {
admin: User.mock().sample({ isActive: true }),
// Para testing de paginación
list: User.mock().array(20, 'random', (idx) => ({
email: `user${idx}@fixture.com`,
})),
// Usuario con datos vacíos para tests de formularios vacíos
empty: User.mock().empty(),
};Modelos con Tipos Complejos
El generador de mocks respeta todos los transformadores:
typescript
interface IReport {
title: string;
createdAt: string;
tags: string[];
metadata: [string, unknown][];
expiresAt: string;
correlationId: string; // Symbol como string
}
@Quick({
createdAt: Date,
tags: Set,
metadata: Map,
expiresAt: Date,
correlationId: Symbol,
})
class Report extends QModel<IReport> {
declare title: string;
declare createdAt: Date;
declare tags: Set<string>;
declare metadata: Map<string, unknown>;
declare expiresAt: Date;
declare correlationId: symbol;
}
const report = Report.mock().random();
console.log(report.createdAt instanceof Date); // true ✅
console.log(report.tags instanceof Set); // true ✅
console.log(report.metadata instanceof Map); // true ✅
console.log(typeof report.correlationId); // 'symbol' ✅Mejores Prácticas
typescript
// ✅ Usa .sample() para tests de snapshot (datos predecibles)
const snapshot = User.mock().sample();
expect(snapshot).toMatchSnapshot();
// ✅ Usa .random() para datos de prueba en unitarios
const user = User.mock().random({ email: 'test@example.com' });
// ✅ Usa .array() para tests de listas/paginación
const page = User.mock().array(10);
// ✅ Usa .interfaceRandom() para preparar fixtures de HTTP mock
fetchMock.mockResponse(JSON.stringify(User.mock().interfaceRandom()));
// ❌ Evitar hardcodear mocks a mano cuando QModel puede generarlos
const user = {
// ❌ propenso a errores, desincronizado del modelo
id: '1',
name: 'test',
createdAt: '2026-01-01', // type: string, no Date
};Próximos Pasos
- Creación en Lote e Inmutabilidad -
createMany()ycreateReadonly() - Validación - Tests con
checkRules()