Я хочу написать такой файл модульного теста, чтобы при каждом его запуске запускался экземпляр всего приложения. Это обеспечивается этой частью:
module = await Test.createTestingModule({
imports: [AppModule],
}).compile();
Затем я захотел очистить базу данных после каждого модульного теста. Он предложил мне использовать:
beforeEach(async () => {
await dataSource.query('BEGIN');
});
afterEach(async () => {
await dataSource.query('ROLLBACK');
});
Но у меня это не хотело работать.
Потом, пообщавшись в чате, я пришел к выводу, что мне следует использовать queryRunner.startTransaction()
и queryRunner.rollbackTransaction()
. Но у меня он все равно не хочет работать.
Пожалуйста, помогите мне исправить приведенный ниже код.
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
import { AppModule } from '../../app.module';
import { DataSource, QueryRunner } from 'typeorm';
import { DATA_SOURCE } from '../database/database.providers';
import { runSeeders } from 'typeorm-extension';
import { ConfigService } from '@nestjs/config';
describe('UsersService', () => {
let service: UsersService;
let module: TestingModule;
let dataSource: DataSource;
let queryRunner: QueryRunner;
let config: ConfigService;
let createQueryRunner;
let release;
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [AppModule],
}).compile();
config = module.get<ConfigService>(ConfigService);
dataSource = module.get<DataSource>(DATA_SOURCE);
if (config.get<string>('NODE_ENV') === 'testing') {
console.info('Running seeders for testing environment');
await runSeeders(dataSource);
}
service = module.get<UsersService>(UsersService);
});
beforeEach(async () => {
queryRunner = dataSource.createQueryRunner();
await queryRunner.startTransaction();
});
afterEach(async () => {
await queryRunner.rollbackTransaction();
await queryRunner.release();
});
afterAll(async () => {
await module.close();
});
it('should be defined', async () => {
await service.create({ firstName: 'FN', lastName: 'LN', email: '[email protected]' });
await service.create({ firstName: 'FN2', lastName: 'LN2', email: '[email protected]' });
expect(service).toBeDefined();
});
it('should return a user', async () => {
const user = await service.findOneByEmail('[email protected]');
expect(user).toBeDefined();
});
});
Путем проб и ошибок я пришел к такому решению. Я использовал комментарии <<==
, чтобы выделить строки, добавленные в предыдущий пример.
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
import { AppModule } from '../../app.module';
import { DataSource, QueryRunner } from 'typeorm';
import { DATA_SOURCE } from '../database/database.providers';
import { runSeeders } from 'typeorm-extension';
import { ConfigService } from '@nestjs/config';
describe('UsersService', () => {
let service: UsersService;
let module: TestingModule;
let dataSource: DataSource;
let queryRunner: QueryRunner;
let config: ConfigService;
let createQueryRunner;
let release;
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [AppModule],
}).compile();
config = module.get<ConfigService>(ConfigService);
dataSource = module.get<DataSource>(DATA_SOURCE);
if (config.get<string>('NODE_ENV') === 'testing') {
console.info('Running seeders for testing environment');
await runSeeders(dataSource);
}
service = module.get<UsersService>(UsersService);
});
beforeEach(async () => {
queryRunner = dataSource.createQueryRunner();
await queryRunner.startTransaction();
createQueryRunner = dataSource.createQueryRunner; // <<==
release = queryRunner.release; // <<==
dataSource.createQueryRunner = () => queryRunner; // <<==
queryRunner.release = () => Promise.resolve(); // <<==
});
afterEach(async () => {
await queryRunner.rollbackTransaction();
dataSource.createQueryRunner = createQueryRunner; // <<==
queryRunner.release = release; // <<==
await queryRunner.release();
});
afterAll(async () => {
await module.close();
});
it('should be defined', async () => {
await service.create({ firstName: 'FN', lastName: 'LN', email: '[email protected]' });
await service.create({ firstName: 'FN2', lastName: 'LN2', email: '[email protected]' });
expect(service).toBeDefined();
});
it('should return a user', async () => {
const user = await service.findOneByEmail('[email protected]');
expect(user).toBeDefined();
});
});
Объяснение:
Я предполагаю, что почти каждый раз, когда делается запрос к базе данных, мы используем dataSource.createQueryRunner().query(<SOME SQL QUERY>)
, который создает новый queryRunner. Но чтобы транзакции работали в нашем тестовом файле, мы должны использовать один и тот же queryRunner
каждый раз, когда делается запрос. Выше показано, как я понял, как обеспечить такое поведение. Если у кого-то есть лучшее решение, пожалуйста, поделитесь им. Я надеюсь, что эта проблема в TypeORM будет исправлена в ближайшее время или будет предложено какое-то лучшее обходное решение.