Skip to content

Testing - Coverage & Quick Reference

Terminal window
# Generate coverage report
npm run test:cov
# View coverage in browser
open coverage/lcov-report/index.html
  • Statements: > 80%
  • Branches: > 75%
  • Functions: > 80%
  • Lines: > 80%
collectCoverageFrom: [
'src/**/*.(t|j)s',
'!src/main.ts', // Bootstrap file
'!src/**/*.module.ts', // Module definitions
'!src/**/*.dto.ts', // Data transfer objects
'!src/**/*.entity.ts', // Database entities
'!src/**/*.enum.ts', // Enums
'!src/**/*.interface.ts', // Type definitions
],

Before submitting tests, verify:

Unit Tests:

  • Mock service layer using factory from test/mocks/
  • Test all controller methods (create, update, findOne, findAll, delete)
  • Test both success and error cases
  • Verify service methods are called with correct arguments
  • Clear mocks in afterEach

E2E Tests:

  • Test full HTTP request/response pipeline
  • Verify routing works correctly
  • Test ValidationPipe (400 for invalid DTOs)
  • Test TransformInterceptor (JSON:API response format)
  • Test AllExceptionsFilter (error responses)
  • Verify response.body structure matches JSON:API spec
  • Close app in afterAll

General:

  • Tests follow AAA pattern (Arrange, Act, Assert)
  • Test names are descriptive
  • Each test focuses on one thing
  • Coverage meets minimum thresholds (>80%)
  • All tests pass locally before pushing

// GET request
await request(server).get('/engagements/123/orders').expect(200);
// POST request with body
await request(server).post('/engagements/123/orders').send(createDTO).expect(201);
// PATCH request
await request(server).patch('/engagements/123/orders/456').send(updateDTO).expect(200);
// DELETE request
await request(server).delete('/engagements/123/orders/456').expect(204);
// With query parameters
await request(server).get('/engagements/123/orders').query({ page: 1, limit: 10 }).expect(200);
// Check response body
const response = await request(server).get('/engagements/123/orders/456').expect(200);
expect(response.body.data.id).toBe('456');
// Mock successful response
mockService.findOne.mockResolvedValue(mockEntity);
// Mock error
mockService.findOne.mockRejectedValue(new NotFoundException());
// Mock different return values for multiple calls
mockService.findOne
.mockResolvedValueOnce({ id: '1' })
.mockResolvedValueOnce({ id: '2' })
.mockResolvedValueOnce({ id: '3' });
// Mock with custom logic
mockService.findOne.mockImplementation(async (id: string) => {
if (id === 'valid-id') {
return { id, name: 'Test' };
}
throw new NotFoundException();
});
// Single resource
expect(response.body).toEqual(
expect.objectContaining({
data: expect.objectContaining({
type: 'engagement-orders',
id: expect.any(String),
attributes: expect.objectContaining({
order_type: 'LAB',
}),
}),
status: expect.objectContaining({ code: 200000 }),
links: expect.objectContaining({ self: expect.any(String) }),
}),
);
// Paginated collection
expect(response.body).toEqual(
expect.objectContaining({
data: expect.any(Array),
meta: expect.objectContaining({
pagination: expect.objectContaining({
page: 1,
page_size: 10,
total: expect.any(Number),
total_pages: expect.any(Number),
}),
}),
links: expect.objectContaining({
self: expect.any(String),
first: expect.any(String),
last: expect.any(String),
}),
}),
);

Standard locations for test artifacts within a bounded context:

Terminal window
apps/[service-name]/
β”œβ”€β”€ jest.config.js # Unit test Jest configuration
β”œβ”€β”€ test/
β”‚ β”œβ”€β”€ jest-e2e.json # E2E test Jest configuration
β”‚ β”œβ”€β”€ mocks/
β”‚ β”‚ └── mock-[entity-name].ts # Mock factory (service + entity + DTO)
β”‚ β”œβ”€β”€ unit/
β”‚ β”‚ β”œβ”€β”€ [entity-name].controller.spec.ts # Controller unit tests
β”‚ β”‚ └── [entity-name].service.spec.ts # Service unit tests
β”‚ └── e2e/
β”‚ └── [entity-name].e2e-spec.ts # E2E tests
└── ...
libs/
└── common/
└── src/
└── testing/
└── test-setup.ts # createTestApp + createTestingModule helpers