Testing Guide
The CoW Protocol BFF uses Jest v29 as its testing framework, integrated with NX for efficient test execution. The setup includes TypeScript support via ts-jest and runs in a Node.js environment.
Running Tests
Basic Commands
# Run all tests across the monorepo
yarn test
# Run tests for a specific project
nx run repositories:test
nx run services:test
nx run api:test
# Watch mode for active development
nx run repositories:test --watch
# Generate coverage reports
nx run repositories:test --coverage
Targeting Specific Files
# Run tests matching a pattern
nx run repositories:test --testPathPattern=usd
# Skip NX cache for fresh execution
nx run repositories:test --skip-nx-cache
Test Organization
File Naming Conventions
- Unit tests:
*.spec.ts
- E2E tests:
*.e2e-spec.ts
Test files should be colocated with the code they test:
libs/repositories/src/
├── usd/
│ ├── UsdRepository.ts
│ ├── UsdRepository.spec.ts
│ ├── UsdRepositoryCache.ts
│ └── UsdRepositoryCache.spec.ts
Test Structure
Tests follow the Given-When-Then pattern:
describe('UsdRepository', () => {
describe('getUsdPrice', () => {
it('should return the price for a valid token', async () => {
// GIVEN - Setup
const repository = new UsdRepositoryImpl(mockClient);
const chainId = SupportedChainId.MAINNET;
const tokenAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
// WHEN - Execute
const price = await repository.getUsdPrice(chainId, tokenAddress);
// THEN - Assert
expect(price).toBeDefined();
expect(typeof price).toBe('number');
expect(price).toBeGreaterThan(0);
});
it('should return null for an unknown token', async () => {
// GIVEN
const repository = new UsdRepositoryImpl(mockClient);
// WHEN
const price = await repository.getUsdPrice(
SupportedChainId.MAINNET,
'0x0000000000000000000000000000000000000000'
);
// THEN
expect(price).toBeNull();
});
});
});
Mocking
Module Mocking
jest.mock('@cowprotocol/shared', () => ({
logger: {
info: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
},
}));
Dependency Mocking
const mockCacheRepository = {
get: jest.fn(),
set: jest.fn(),
};
const mockUsdRepository = {
getUsdPrice: jest.fn(),
getUsdPrices: jest.fn(),
};
Method Spying
const spy = jest.spyOn(repository, 'getUsdPrice');
await service.calculateSlippage(chainId, baseToken, quoteToken);
expect(spy).toHaveBeenCalledWith(chainId, baseToken);
Note: Mocks must be “hoisted” (placed at the top level before relevant imports).
Coverage
Generate coverage reports:
nx run repositories:test --coverage
Coverage results are output to project-specific directories.
NX Caching
NX provides intelligent caching to avoid re-running unchanged tests:
# Force fresh test execution (skip cache)
nx run repositories:test --skip-nx-cache
# Clear all NX cache
nx reset
Integration Testing
Testing with Database (TypeORM)
describe('IndexerStateRepository', () => {
let dataSource: DataSource;
beforeAll(async () => {
dataSource = await createTestDataSource();
await dataSource.runMigrations();
});
afterAll(async () => {
await dataSource.destroy();
});
it('should persist and retrieve state', async () => {
const repo = new IndexerStateRepositoryOrm(dataSource);
await repo.save('test-key', 1, { lastBlock: '100' });
const state = await repo.get('test-key', 1);
expect(state).toEqual({ lastBlock: '100' });
});
});
Testing API Endpoints (Fastify)
describe('GET /about', () => {
let app: FastifyInstance;
beforeAll(async () => {
app = await buildApp();
});
afterAll(async () => {
await app.close();
});
it('should return version information', async () => {
const response = await app.inject({
method: 'GET',
url: '/about',
});
expect(response.statusCode).toBe(200);
const body = JSON.parse(response.body);
expect(body).toHaveProperty('name');
expect(body).toHaveProperty('version');
});
});
Testing Notifications
Send test notifications to verify the full pipeline:
POST_TO_QUEUE_ACCOUNT=0x79063d9173C09887d536924E2F6eADbaBAc099f5 \
nx test notification-producer \
--testFile=src/sendPush.test.ts \
--skip-nx-cache
CI Integration
GitHub Actions automatically runs tests on commits. The CI configuration uses NX’s affected command to only test changed projects:
nx affected --target=test
Best Practices
- Test behavior, not implementation - Focus on inputs and outputs
- Use descriptive test names - Describe the scenario being tested
- One assertion concept per test - Keep tests focused
- Follow Given-When-Then - Structure tests clearly
- Colocate tests with code - Keep test files next to implementation
- Use factory functions for test data creation
- Clean up resources in
afterAll/afterEach hooks
- Avoid testing private methods - Test through public interfaces
Last modified on March 4, 2026