NestJS单元测试报错:this.httpService.post is not a function 求助
问题:使用jest.spyOn模拟HttpService.post时触发TypeError: this.httpService.post is not a function
我在为返回Promise的auth.service.ts编写单元测试,想通过jest.spyOn编写成功响应和错误场景的测试用例,但执行jest.spyOn(httpService, 'post').mockImplementationOnce(() => mockResponse)时触发TypeError: this.httpService.post is not a function错误;不使用spyOn时测试能正常运行。
AuthService代码
import { HttpService } from '@nestjs/axios'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '../config/config.service'; import { AppBuilderToolLogger } from '../config/logger.module'; import { ApiConstants } from '../constants/api-constants'; @Injectable() export class AuthService { constructor( private readonly logger: AppBuilderToolLogger, private readonly httpService: HttpService, private readonly configService: ConfigService, ) {} /** * getUspBasedOnSessionID fucntion call api and return a response * * @param cookie * @returns */ async getUspBasedOnSessionID(cookie: string): Promise<any> { const data = await new Promise((resolve) => { this.httpService .post<any>( this.configService.get('COMMON_API_URL') + ApiConstants.userGetUspBasedOnSessionID, '', { headers: { Cookie: cookie, }, }, ) .subscribe({ next: (response) => { resolve(response.data); }, error: (error) => { this.logger.error('Calling error getUspBasedOnSessionID()', error); resolve('An error happened on getUspBasedOnSessionID!'); }, }); }); return data; } }
原测试代码
import { Test, TestingModule } from '@nestjs/testing'; import { describe, expect } from '@jest/globals'; import { AuthService } from './auth.service'; import { AppBuilderToolLogger, ConfigModule, ConfigService } from '../config'; import { HttpModule } from '@nestjs/axios'; import { HttpService } from '@nestjs/axios'; describe('AuthService', () => { let service: AuthService; let httpService: HttpService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [HttpModule], providers: [ AuthService, { provide: HttpService, useValue: {} }, AppBuilderToolLogger, { provide: ConfigService, useValue: new ConfigService(`.env.dev`), }, ], }).compile(); service = module.get<AuthService>(AuthService); httpService = module.get<HttpService>(HttpService); }); it('should be defined', () => { expect(service).toBeDefined(); }); describe('getUspBasedOnSessionID function', () => { it('post request successfully ', async () => { const mockData = {"name": 'test'}; const mockResponse = <any> {data: mockData}; // jest.spyOn(httpService, 'post').mockImplementationOnce(() => mockResponse); service.getUspBasedOnSessionID('random-cookie').then((data) => { expect(data).toEqual(mockData); }); }); }); });
不使用spyOn时的正常测试代码
it('should be defined', () => { expect(service).toBeDefined(); }); describe('getUspBasedOnSessionID function', () => { it('getUspBasedOnSessionID should not return null', async () => { const cookie = 'random-cookie'; const result = await service.getUspBasedOnSessionID(cookie); expect(result).not.toBeNull(); expect(result).toBe('An error happened on getUspBasedOnSessionID!'); }); });
问题原因与解决方案
问题根源
测试模块的依赖注入配置错误:你手动提供了HttpService的空对象useValue: {},这个对象没有post方法,导致jest.spyOn找不到要监听的方法,触发报错。同时,HttpService.post返回的是RxJS Observable,原模拟的普通对象也不符合返回类型要求。
修复步骤
- 移除手动提供的空HttpService:已导入
HttpModule,它会自动提供HttpService实例,无需手动覆盖。 - 正确模拟Observable返回值:用RxJS的
of模拟成功响应,throwError模拟错误响应,匹配HttpService.post的返回类型。
修复后的完整测试代码
import { Test, TestingModule } from '@nestjs/testing'; import { describe, expect, jest } from '@jest/globals'; import { AuthService } from './auth.service'; import { AppBuilderToolLogger, ConfigService } from '../config'; import { HttpModule } from '@nestjs/axios'; import { HttpService } from '@nestjs/axios'; import { of, throwError } from 'rxjs'; describe('AuthService', () => { let service: AuthService; let httpService: HttpService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [HttpModule], providers: [ AuthService, AppBuilderToolLogger, { provide: ConfigService, useValue: new ConfigService(`.env.dev`), }, ], }).compile(); service = module.get<AuthService>(AuthService); httpService = module.get<HttpService>(HttpService); }); it('should be defined', () => { expect(service).toBeDefined(); }); describe('getUspBasedOnSessionID function', () => { it('should return mock data when post request succeeds', async () => { const mockData = { "name": 'test' }; const mockResponse = { data: mockData }; // 模拟post方法返回成功的Observable jest.spyOn(httpService, 'post').mockReturnValueOnce(of(mockResponse)); const result = await service.getUspBasedOnSessionID('random-cookie'); expect(result).toEqual(mockData); expect(httpService.post).toHaveBeenCalled(); }); it('should return error message when post request fails', async () => { const mockError = new Error('API Error'); // 模拟post方法返回错误的Observable jest.spyOn(httpService, 'post').mockReturnValueOnce(throwError(() => mockError)); // 模拟logger.error避免控制台输出测试日志 jest.spyOn(service['logger'], 'error').mockImplementation(); const result = await service.getUspBasedOnSessionID('random-cookie'); expect(result).toBe('An error happened on getUspBasedOnSessionID!'); expect(httpService.post).toHaveBeenCalled(); expect(service['logger'].error).toHaveBeenCalled(); }); }); });
额外提示
- 测试异步方法时用
await替代.then(),让逻辑更清晰,避免异步断言遗漏。 - 模拟
logger.error可以防止测试时控制台输出冗余日志,保持测试输出干净。
内容的提问来源于stack exchange,提问作者Lency Bhalodiya
相关产品推荐
相关产品推荐

