@storm-trade/jest-config v1.0.3
testing
Библиотека для помощи в написании тестов на node.js.
Экспортирует несколько хеплеров:
[TOC]
defaultConfig
Дефолтный конфиг для юнит и интеграционных тестов. Старайтесь не добавлять в него проект-специфичные опции.
Использование: в файл jest.config.js
напишите код:
const { createDefaultConfig } = require('@atmruback/testing');
module.exports = {
...createDefaultConfig(),
};
Если проект находится на стадии поднятия test coverage, то стоит зафиксировать coverageThreshold на текущем уровне, чтобы не происходила деградация. Параметр coverageThreshold можно установить как для global, так и для отдельных директорий. Пример:
const { createDefaultConfig } = require('@atmruback/testing');
module.exports = {
...createDefaultConfig(),
coverageThreshold: {
global: {
statements: 30,
},
'./src/work-days/': {
statements: 40,
}
}
};
Подробнее: https://jestjs.io/docs/configuration#coveragethreshold-object
Для интеграционных тестов в файле src/utils/test-utils/setup-integrations-db.ts
напишите код:
import { initializeDatabase } from '@atmruback/testing/int/init-db';
import { dataSourceOptions } from '../../datasource';
export default async (): Promise<void> => initializeDatabase(dataSourceOptions);
@atmruback/testing/int/mock-rmq
- мокает взаимодействие с RabbitMQ.
@atmruback/testing/int/init-db
- создаёт базу данных под каждый воркер jest. Это необходимо, чтобы тесты не мешали друг другу при параллельном запуске.
Количество воркеров определяется переменной окружения JEST_MAX_WORKERS
.
После этого в файле интеграционного теста *.int.spec.ts
вам нужно будет указать для typeorm название базы данных под текущий воркер.
Пример:
import { workerDatabaseName } from '@atmruback/testing/int/init-db';
//...
const moduleRef = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot(typeOrmModuleConfig({ database: workerDatabaseName })),
],
})
Для запуска всех тестов (интеграционных и юнит) используем команду:
DOTENV_CONFIG_PATH=.env.test node -r dotenv/config ./node_modules/.bin/jest --force-exit --coverage
Для запуска только юнит-тестов используем команду:
DOTENV_CONFIG_PATH=.env.test node -r dotenv/config ./node_modules/.bin/jest --force-exit --coverage --selectProjects unit
Для запуска только интеграционных тестов используем команду:
DOTENV_CONFIG_PATH=.env.test node -r dotenv/config ./node_modules/.bin/jest --force-exit --coverage --selectProjects int
Если энвы для данного проекта не применимы (библиотеки) то из команд запуска убираем подключение dotenv:
node ./node_modules/.bin/jest --force-exit --coverage
или
jest --force-exit --coverage
provideServiceDefaultMock
provideRepositoryDefaultMock
Создают моки сервиса/репозитория для использования в зависимостях для тестирования nest сервиса. Возвращают объект типа MockProxy<>. См. ниже "Библиотека jest-mock-extended
".
provideRepositoryDefaultMock
вынесена в файл @atmruback/testing/repository-mock-factory
, чтобы не требовать от потребителей подключения @nestjs/typeorm
.
Пример использования:
import { LoggerService } from '@atmruback/logger';
import { MockProxy, provideServiceDefaultMock } from '@atmruback/testing';
import { provideRepositoryDefaultMock } from '@atmruback/testing/repository-mock-factory';
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { MyService } from './my.service';
describe('myService', () => {
let service: MyService;
let myRepository: MockProxy<Repository<MyEntity>>;
let loggerService: MockProxy<LoggerService>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [MyService, provideRepositoryDefaultMock(MyEntity), provideServiceDefaultMock(LoggerService)],
}).compile();
service = module.get<MyService>(MyService);
myRepository = module.get(getRepositoryToken(MyEntity));
loggerService = module.get(LoggerService)
});
it('should do action', async () => {
const criticalMock = loggerService.critical.calledWith('test').mockReturnValue('stub');
const saveMock = myRepository.save.mockResolvedValue(<MyEntity>{});
await service.doAction();
expect(criticalMock).toHaveBeenCalledOnce();
expect(saveMock).toHaveBeenCalledOnceWith({ testField: "testValue" });
});
guardMock
createJwtGuardMock
interceptorMock
Моки гардов и интерцепторов для интеграционных тестов. createJwtGuardMock создаёт новый мок jwt гарда с проставлением payload'а. Пример:
const moduleRef = await Test.createTestingModule({
imports: [...],
})
.overrideGuard(JwtGuard)
.useValue(createJwtGuardMock({ id: testUserId, aud: TokenAudience.ACCOUNT }))
.overrideGuard(MfaGuard)
.useValue(guardMock)
.overrideGuard(AuditLogMfaInterceptor)
.useValue(interceptorMock)
.compile();
Библиотека @total-typescript/shoehorn
@atmruback/testing
реэкспортирует методы из @total-typescript/shoehorn
.
Решает проблему передачи в функции параметра, который удовлетворяет типу лишь частично. Ранее мы использовали для этого конструкцию {} as MyType
.
Это не очень хорошо: мы привыкаем к нормальности as
, который не является типобезопасным; и всегда нужно указывать конкретный тип.
Функции:
fromPartial
- позволяет передать в функцию параметр с неполным набором полей. Кинет ошибку, если тип параметра не соответствует ожидаемому. Заменяет{} as MyType
.fromAny
- позволяет передать в функцию параметр любого типа. НЕ кинет ошибку, если тип параметра не соответствует ожидаемому. Заменяет{} as unknown as MyType
.fromExact
- заставляет указать весь набор полей для параметра. Полезно, при переключении междуfromPartial
/fromAny
или когда хотите их удалить.
Пример использования:
import { fromPartial } from "@atmruback/testing";
requiresRequest(
fromPartial({
body: {
id: "123",
},
}),
);
Подробнее: https://www.npmjs.com/package/@total-typescript/shoehorn
Библиотека jest-extended
@atmruback/testing
реэкспортирует jest-extended
.
Добавляет набор матчеров для jest: https://jest-extended.jestcommunity.dev/docs/matchers
Нужно импортировать '@atmruback/testing' и матчеры будут добавлены в глобальный скоуп.
Пример использования:
import '@atmruback/testing';
describe('myService', () => {
it('should do action', async () => {
expect(null).toBeNil();
expect(undefined).toBeNil();
expect(true).not.toBeNil();
});
Подробнее: https://www.npmjs.com/package/jest-extended
Библиотека jest-mock-extended
@atmruback/testing
реэкспортирует методы и типы из jest-mock-extended
.
Библиотека для генерации типобезопасных моков для объектов и интерфейсов.
Пример:
import { MockProxy, mock } from '@atmruback/testing';
class MyClass {
public fooBar(text: string): number {
return text.length;
}
}
describe('test', () => {
let myMock: MockProxy<MyClass>;
beforeEach(() => {
myMock = mock<MyClass>();
});
it('should do action', () => {
myMock.fooBar.calledWith('foo').mockReturnValue(1);
myMock.fooBar.calledWith('bar').mockReturnValue(2);
expect(myMock.fooBar('foo')).toBe(1);
expect(myMock.fooBar('bar')).toBe(2);
expect(myMock.fooBar('foobar')).toBeUndefined();
});
});
});