You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,原模拟的普通对象也不符合返回类型要求。

修复步骤

  1. 移除手动提供的空HttpService:已导入HttpModule,它会自动提供HttpService实例,无需手动覆盖。
  2. 正确模拟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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 12:55:58