typescript - NestJS enhetstesting av tjenesten: Mocking getManager som resulterer i feilen 'Connection "default" ble ikke funnet'
Jeg tester for øyeblikket enhetstesting av min NestJS-tjeneste. Min enhet kalles 'User', og jeg har etablert en grunnleggende tjeneste som lar meg samhandle med en MS SQL-server, med GET- og POST-endepunkter etablert i min Controller.
En av tjenestemetodene jeg prøver å etterligne er deleteV2-funksjonen, som du kan finne nedenfor:
// service file
import { Injectable } from '@nestjs/common';
import { InjectConnection, InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
import { Connection, createConnection, EntityManager, getConnection, getManager, getRepository, Repository } from "typeorm";
import {User} from '../entities/user.entity';
@Injectable()
export class ServiceName {
constructor(@InjectRepository(User) private usersRepository: Repository<User>,
@InjectConnection() private connection: Connection,
@InjectEntityManager() private manager: EntityManager
) {}
async delete(id: number): Promise<any> {
return await this.manager.delete(User, id);
}
async deleteV2(id: number): Promise<any> {
return await getManager().delete(User, id);
}
Merk at grunnen til at jeg har importert EntityManager OG injisert (med @InjectEntityManager()) en manager: EntityManager i tjenesten min, er den enkle årsaken at jeg ønsket å få litt mer øvelse med å jobbe med forskjellige metoder (jeg er en ny intern som jobber med NestJS). Faktumet at jeg har delete-metoden definert der jeg bruker den injiserte manager og deretter deleteV2-metoden definert der jeg bruker den importerte getManager er også rett og slett for å øve på enhetstesting med forskjellige metoder. Og det faktum at jeg injiserer Repository, Connection og EntityManager, er også av samme grunn.
I min spec.ts enhetstestfil for tjenesten har jeg:
// unit testing file for the service
type MockType<T> = {
[P in keyof T]?: jest.Mock<{}>;
};
describe('service tests', () => {
const mockManagerFactory = jest.fn(() => ({
delete: jest.fn().mockReturnValue(undefined),
}))
let service: ServiceName;
// mock repository and mock connection also defined similarly to mockManager
let mockManager: MockType<EntityManager>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ServiceName,
// tokens for Repository and Connection are also provided along with their respective Factories
{
provide: getEntityManagerToken(),
useFactory: mockManagerFactory,
},
],
}).compile();
service = module.get<ServiceName>(ServiceName);
// mockRepository and mockConnection also defined but not shown
mockManager = module.get(getEntityManagerToken());
});
})
Når jeg holder meg på den samme spec.ts-filen, definerte jeg flere tester. Den som forårsaker problemer er den som involverer deleteV2 nedenfor:
// same unit testing file for the service
// first test
it('first test', async () => {
expect(await service.delete(1000)).toEqual(undefined);
expect(mockManager.delete).toBeCalled();
});
// second test
it('second test', async () => {
expect(await service.deleteV2(1000)).toEqual(undefined);
expect(mockManager.delete).toBeCalled();
})
Mens den første testen ikke har problemer med å kjøre (den første testen er knyttet til delete i tjenestefilen), består ikke den andre testen (knyttet til deleteV2 i tjenestefilen) og resulterer i følgende feil:
● service tests › Service Functions › second test
ConnectionNotFoundError: Connection "default" was not found.
at new ConnectionNotFoundError (error/ConnectionNotFoundError.ts:8:9)
at ConnectionManager.Object.<anonymous>.ConnectionManager.get (connection/ConnectionManager.ts:40:19)
at Object.getManager (index.ts:260:35)
Noen ideer om hvordan man kan håndtere denne feilen? Det virker som om å bruke getEntityManagerToken() fungerer med den injiserte manager, men ikke med getManager som er importert, siden deleteV2 bruker getManager mens delete (som består testen) bruker den importerte manager
Svar #1
Dette er hvor en stor fordel med å bruke avhengighetsinjeksjon kommer inn. @InjectEntityManager() injiserer bare objektet som (teoretisk) allerede har forbindelsen satt opp for deg, og du kan bare bruke metodene uten å bekymre deg om forbindelsen. Det gjør testing betydelig enklere (etter min mening). Med getMananger() må du bekymre deg for at TypeORM prøver å hente entity-manageren fra den aktive forbindelsen, som ikke burde være der i tester. Du kan mocke dette ved å bruke jest ved å bruke noe som følgende
const deleteMock = jest.fn();
jest.mock('typeorm', () => ({
getManager: () => ({
delete: deleteMock,
})
}))
Dette fungerer godt nok, men jeg vil unngå å kalle metoder som getConnection og getManager fra TypeORM direkte hvis mulig.
