typescript - NestJS: HttpService není v jednotkovém testu namockován ani nenalezen
Snažím se napsat jednotkový test pro službu @nestjs a potřebuji mockovat HttpService. Moje implementace jednotkového testu vrací následující chybu:
TypeError: Cannot read properties of undefined (reading 'get')V kontextu jednotkového testu není httpService nikdy definováno, přestože je v modulu air-gateway.service použit dekorátor @Injectable.
air-gateway.service.spec.ts
import { AirGatewayService } from './air-gateway.service';
import { HttpService } from '@nestjs/axios';
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { Test, TestingModule } from '@nestjs/testing';
describe('AirGateway Service', () => {
let service: AirGatewayService;
let httpService: DeepMocked<HttpService>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
AirGatewayService,
{
provide: HttpService,
useValue: createMock<HttpService>(),
},
],
})
.useMocker(createMock)
.compile();
service = await module.get<AirGatewayService>(AirGatewayService);
httpService = await module.get(HttpService);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
Zde je implementace služby. Mohu potvrdit, že úspěšně volá koncový bod v mém vývojářském prostředí.
air-gateway.service.ts
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { TerminusArgs } from './dto/terminus.args';
import { AirSchedules } from './models/air-schedules.model';
@Injectable()
export class AirGatewayService {
constructor(private readonly httpService: HttpService) {}
public async findAll(terminusArgs: TerminusArgs): Promise<AirSchedules> {
const { data } = await firstValueFrom(
this.httpService
.get<GatewayResponse<AirSchedules>>(gatewayURL.href, { headers })
.pipe(
catchError((error: AxiosError) => {
throw `GatewayServiceError -> findAll: an error has occurred fetching for Gateway\n${error}`;
})
)
);
}
}
Aktualizoval jsem závislosti pro cokoli spojené s Nest, axios, jest, typescript na nejnovější verze.
"@nestjs/apollo": "12.0.3","@nestjs/axios": "^3.0.0","@nestjs/core": "10.0.3","typescript": "5.1.6",Mám soubor babel.config.js pro můj projekt, aby mi umožnil spouštět TypeScript během mých testů
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
'@babel/preset-typescript'
],
plugins: [['@babel/plugin-proposal-decorators', { version: '2023-11' }]]
};
- Zkoušel jsem nastavit a odstranit soubor
jest.config.jstakto:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleNameMapper: {
axios: 'axios/dist/node/axios.cjs'
}
}
- Experimentoval jsem s npm balíčkem
@automock/jest, ale to prostě selhává kvůli prakticky stejné chybě, jen vyjádřené jinak v chybových záznamech.
Automock úryvek
let service: AirGatewayService;
let httpService: jest.Mocked<HttpService>;
beforeAll(() => {
const { unit, unitRef } =
TestBed.create(AirGatewayService).compile();
service = unit;
httpService = unitRef.get(HttpService);
});
Selže s: IdentifierNotFoundError: Chybějící identifikátor závislosti třídy (kód ER010) Nelze najít závislost spojenou se zadaným tokenem nebo identifikátorem ('HttpService') v aktuálním testovacím kontextu. Tento problém se týká použití API UnitReference. Ujistěte se, že je přesně napsáno a že poskytovaný token nebo identifikátor odpovídá příslušné konfiguraci injekce. Pokud používáte vlastní token, je nezbytné potvrdit jeho správnou registraci v DI kontejneru.
- Dostávám tento výpis při spuštění
npx jest air-gateway.service.spec.tss povoleným sledováním.
Trace: gateway
at GatewayService.trace [as findAll] (/Users/pingwinZloty/Projects/centrus-active/apps/gateway-subgraph/src/gateway/gateway.service.ts:41:9)
at Object.findAll (/Users/pingwinZloty/Projects/centrus-active/apps/gateway-subgraph/src/gateway/tests/gateway.service.spec.ts:140:35)
at Promise.then.completed (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-circus/build/utils.js:298:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-circus/build/utils.js:231:10)
at _callCircusTest (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-circus/build/run.js:316:40)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at _runTest (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-circus/build/run.js:252:3)
at _runTestsForDescribeBlock (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-circus/build/run.js:126:9)
at _runTestsForDescribeBlock (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-circus/build/run.js:121:9)
at run (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-circus/build/run.js:71:3)
at runAndTransformResultsToJestFormat (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
at jestAdapter (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
at runTestInternal (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-runner/build/runTest.js:367:16)
at runTest (/Users/pingwinZloty/Projects/centrus-active/node_modules/jest-runner/build/runTest.js:444:34)
Odpověď č. 1
Kód se nesestavuje, protože chybí soubory. Proto jsem nahradil konstruktor třídy AirGatewayService verzí, která provádí console.log(this.httpService), abych mohl reprodukovat chybu. Můžete vidět, že this.httpService je prázdný objekt. Zřejmě nemockujete metodu get(), a to je váš problém.
Nevím o @golevelup/ts-jest, ale službu můžete otestovat pomocí klasického NestJS a Jest. Definujte svůj testovací modul v air-gateway.service.spec.ts takto:
let service: AirGatewayService;
let httpService: HttpService;
const testScheduler = new TestScheduler(() => {});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
AirGatewayService,
{
provide: HttpService,
useValue: {
get: jest.fn(),
},
},
],
}).compile();
service = module.get<UniversitiesService>(UniversitiesService);
httpService = module.get<HttpService>(HttpService);
});
Konstruktér nyní dostává injektovanou maketovanou HttpService.
Službu můžete otestovat tímto způsobem:
it('should return all air schedules', () => {
const data: AirSchedules = {
// Your data goes here ...
};
const spy = jest
.spyOn(httpService, 'get')
.mockReturnValue(of({
data,
headers: {},
config: {
url: 'http://localhost/what/ever',
headers: {} as AxiosRequestHeaders,
},
status: 200,
statusText: 'OK',
})
);
terminusArgs: TerminusArgs = YOUR_ARGS_GO_HERE;
testScheduler.run(() => {
await expect(service.findAll(terminusArgs)).resolves.toEqual(data);
expect(spy).toHaveBeenCalledTimes(1);
});
});
Podrobně jsem vysvětlil testování NestJS HttpService v příspěvku na blogu Pitfalls in Testing NestJS Modules with the @nestjs/axios HttpService. K dispozici je také doprovodné git repozitář gflohr/test-nestjs-http-service s kompletním zdrojovým kódem. Malý rozdíl je pouze v tom, že používám Observable a ne Promise, ale obecná myšlenka by měla fungovat s oběma přístupy.
Je také zásadní spustit test uvnitř TestSchedule z rxjs/testing. Jinak můžete narazit na asynchronní problémy s Jest.
Odpověď č. 2
Zkuste předstírat použití overrideProvider
const module: TestingModule = await Test.createTestingModule({
providers: [
AirGatewayService,HttpService
],
})
.overrideProvider(HttpService)
.useValue(mockHttpService)
.compile();
Své HttpService můžete zesměšnit takto
export const mockHttpService ={
get: jest.fn().mockImplementation(() => {
return ;
}),
}
