superTest (superAgent)
Most of our tests are integration tests, they use the databases.
Prefix our entities / collections with the id of the jest worker.
// setup.ts
process.env.TYPEORM_ENTITY_PREFIX = process.env.JEST_WORKER_ID;
process.env.MONGO_SCHEMA_PREFIX = process.env.JEST_WORKER_ID;
// jest.config.js
module.exports = {
// ...
setupFiles: ['<rootDir>/setup.ts']
}
// schema.decorator.ts
import { Schema as DefaultSchema } from '@nestjs/mongoose';
import { SchemaOptions } from 'mongoose';
export const Schema = (options: SchemaOptions): ClassDecorator =>
DefaultSchema({
...options,
...(options.collection !== undefined
? { collection: (process.env.MONGO_SCHEMA_PREFIX ?? '') + options.collection }
: {}),
});
Factor transpilation and run Jest on js code
Factor transpilation and run Jest on js code
// jest.config.js
module.exports = {
testEnvironment: 'node',
rootDir: 'dist',
collectCoverageFrom: [
'<rootDir>/src/**/*.js',
'!<rootDir>/src/main.js',
'!<rootDir>/src/modules/logger/**',
'!<rootDir>/src/**/*.d.ts',
],
setupFiles: ['<rootDir>/tests/setup.js']
};
// tsconfig.json
{
// no tests here
"exclude": ["node_modules", "dist", "dangerfile.ts", "dangerRules"]
}
Local
CI
// jest.config.js
module.exports = {
// ...
cacheDirectory: '<rootDir>/../.jest-cache'
};
// CI.yaml
name: CI
jobs:
ci:
steps:
// checkout code, install dependencies, ...
- uses: actions/cache@v2
with:
path: .jest-cache
key: ${{ runner.os }}-jest-cache-${{ hashFiles('**/*.test.ts') }}
restore-keys: |
${{ runner.os }}-jest-cache-${{ hashFiles('**/*.test.ts') }}
${{ runner.os }}-jest-cache-
- run: yarn test:coverage
// ...
Install dependencies
Build
code
Run
Tests
// package.json
{
// ...
"scripts": {
// ...
"start-db": "docker-compose -f docker-compose.test.yml up -d",
"test": "yarn start-db && dotenv -e .env.test -- node --expose-gc --max_old_space_size=1024 ./node_modules/.bin/jest --maxWorkers=4 --logHeapUsage",
"test:coverage": "yarn test --coverage --reporters=default --watchAll=false --testResultsProcessor jest-sonar-reporter",
},
}
Pull docker images
Start dockers
Run
tests
Install dependencies
Build
code
Run
Tests
Find docker images
Start dockers
Run
tests
Pull docker images (detached)
beforeEach(async () => {
await createAndSaveUser(userModel, {
TUWIS: [well.tuwi],
});
});
afterEach(async () => {
await userModel.deleteMany({});
});
In each test file, grouping tests that need a user to create the user only once per group
The idea
Before
beforeEach(async () => {
await createAndSaveUser(userModel, {
TUWIS: [well.tuwi],
});
});
afterEach(async () => {
await userModel.deleteMany({});
});
beforeEach(async () => {
await createAndSaveUser(userModel, {
TUWIS: [well.tuwi],
});
});
afterEach(async () => {
await userModel.deleteMany({});
});
After
describe('without any authenticated user', () => {
// tests
});
describe('with an authenticated user', () => {
beforeAll(async () => {
await createAndSaveUser(userModel, {
TUWIS: [well.tuwi],
});
});
afterAll(async () => {
await userModel.deleteMany({});
});
// tests
});
Reducing the number of creations for other entities (eg: wells)
Move a part of our tests to unit tests
We were mostly doing integration tests because our services require a lot of injected stuf. And the things we needed to inject are hard to mock (complex services)
import { createMock } from '@golevelup/ts-jest';
import { ExecutionContext } from '@nestjs/common';
const mockExecutionContext = createMock<ExecutionContext>();
https://github.com/golevelup/nestjs/tree/master/packages/testing
@golevelup/ts-jest
Profile our tests to identify other recurrent tasks
Use the new sharding feature of Jest
450
tests
830
tests
https://github.com/LeoAnesi/github-ci-analysis