IMPORTANTE: Todos los tests deben estar en la carpeta /test, NUNCA junto al código fuente.
test/
├── unit/
│ └── modules/
│ ├── health/
│ │ ├── health.controller.spec.ts
│ │ └── health.service.spec.ts
│ ├── auth/
│ │ ├── auth.controller.spec.ts
│ │ └── auth.service.spec.ts
│ ├── users/
│ │ ├── users.controller.spec.ts
│ │ └── users.service.spec.ts
│ └── [otro-modulo]/
├── e2e/
│ └── modules/
│ ├── health/
│ │ └── health.e2e-spec.ts
│ └── [otro-modulo]/
├── fixtures/
│ └── index.ts # Factories y datos de prueba reutilizables
└── helpers/
└── index.ts # Utilidades para tests
Cada módulo debe tener su propia carpeta dentro de test/unit/modules/[nombre-modulo]/ y test/e2e/modules/[nombre-modulo]/.
Ejemplo: Para el módulo health:
- Tests unitarios:
test/unit/modules/health/ - Tests e2e:
test/e2e/modules/health/
- Unit tests:
[nombre].controller.spec.ts,[nombre].service.spec.ts - E2E tests:
[nombre].e2e-spec.ts - Usar kebab-case para nombres de archivos
- Unit tests (
test/unit/): Testean componentes aislados (controllers, services, utils) - E2E tests (
test/e2e/): Testean flujos completos de la aplicación - Fixtures (
test/fixtures/): Datos de prueba reutilizables y factories - Helpers (
test/helpers/): Utilidades para tests (mocks, helpers de autenticación, etc.)
Ubicación: test/unit/modules/[modulo]/[modulo].controller.spec.ts
Responsabilidades:
- Mockear servicios
- Testear validación de entrada
- Testear respuestas HTTP
- Testear manejo de errores
Ejemplo:
describe('HealthController', () => {
let controller: HealthController;
let healthService: HealthService;
const mockHealthService = {
check: jest.fn(),
};
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [HealthController],
providers: [
{ provide: HealthService, useValue: mockHealthService },
],
}).compile();
controller = module.get<HealthController>(HealthController);
});
it('should return health status', async () => {
const expectedResult = { status: 'ok' };
mockHealthService.check.mockResolvedValue(expectedResult);
const result = await controller.check();
expect(result).toEqual(expectedResult);
});
});Ubicación: test/unit/modules/[modulo]/[modulo].service.spec.ts
Responsabilidades:
- Mockear dependencias (repositories, otros servicios)
- Testear lógica de negocio
- Testear casos de error
- Testear validaciones
Ejemplo:
describe('HealthService', () => {
let service: HealthService;
let supabaseService: SupabaseService;
const mockSupabaseService = {
getClient: jest.fn(),
};
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
HealthService,
{ provide: SupabaseService, useValue: mockSupabaseService },
],
}).compile();
service = module.get<HealthService>(HealthService);
});
it('should return health status', async () => {
const result = await service.check();
expect(result).toHaveProperty('status', 'ok');
});
});Ubicación: test/unit/utils/[util].spec.ts
Responsabilidades:
- Tests puros sin dependencias
- Testear funciones utilitarias
Ubicación: test/e2e/modules/[modulo]/[modulo].e2e-spec.ts
Responsabilidades:
- Testear flujos completos
- Testear integración con base de datos
- Testear endpoints completos
Ejemplo:
describe('HealthController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/health (GET)', () => {
return request(app.getHttpServer())
.get('/health')
.expect(200)
.expect((res) => {
expect(res.body).toHaveProperty('status', 'ok');
});
});
});Ubicación: test/fixtures/index.ts
Uso: Crear factories y datos de prueba reutilizables
Ejemplo:
export const createMockUser = (overrides?: Partial<User>): User => ({
id: '123',
email: 'test@example.com',
name: 'Test User',
...overrides,
});Ubicación: test/helpers/index.ts
Uso: Utilidades para tests (mocks, helpers de autenticación, etc.)
Ejemplo:
export const createTestModule = async (providers: any[]) => {
return Test.createTestingModule({ providers }).compile();
};- Un archivo
.spec.tspor archivo fuente - Usar
describeeitpara organización - Mockear todas las dependencias externas
- Tests independientes y determinísticos
- Usar
beforeEachyafterEachpara setup/cleanup - Nombres descriptivos:
it('should return error when user not found')
# Ejecutar todos los tests
npm test
# Ejecutar tests en modo watch
npm run test:watch
# Ejecutar tests con coverage
npm run test:cov
# Ejecutar solo tests e2e
npm run test:e2edescribe('ModuleService', () => {
let service: ModuleService;
let mockDependency: MockDependency;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
ModuleService,
{ provide: Dependency, useValue: mockDependency },
],
}).compile();
service = module.get<ModuleService>(ModuleService);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('methodName', () => {
it('should do something', async () => {
// Arrange
const input = 'test';
mockDependency.method.mockResolvedValue('result');
// Act
const result = await service.methodName(input);
// Assert
expect(result).toBe('result');
expect(mockDependency.method).toHaveBeenCalledWith(input);
});
});
});- ✅ SIEMPRE poner tests en
/test, nunca en/src - ✅ Organizar por módulos dentro de
test/unit/modules/ytest/e2e/modules/ - ✅ Un archivo de test por archivo fuente
- ✅ Mockear todas las dependencias externas
- ✅ Tests deben ser independientes y determinísticos