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

Jest SpyOn无法Mock类方法问题求助

Jest无法Mock类实例方法的问题解决方案

问题根源

  1. 测试中spyOn的pokemonService实例,与EvolutionService内部自行创建的PokemonService实例是完全独立的两个对象,mock前者的方法不会作用到后者。
  2. getByName是用箭头函数定义的实例属性方法,直接挂载在每个实例上而非类的原型上,常规的原型Spy方式不适用。

方案一:依赖注入(推荐,符合开闭原则)

修改EvolutionService,允许通过构造函数注入PokemonService实例,测试时传入预先mock好的实例:

修改/evolution/evolution.service.ts

import PokemonService from "../pokemon/pokemon.service";
import { IPokemon } from "../types/types";

export default class EvolutionService {
  private pokemonService: PokemonService;
  // 新增参数,默认创建实例,不影响原有业务逻辑
  constructor(pokemonService?: PokemonService) {
    this.pokemonService = pokemonService ?? new PokemonService();
  }

  public chainByName = async (name: string) => {
    const pokemon: IPokemon = await this.pokemonService.getByName(name);
    return pokemon;
  };
}

修改测试文件/evolution/evolution.service.spec.ts

import EvolutionService from "./evolution.service";
import PokemonService from "../pokemon/pokemon.service";

// 创建实例并mock方法
const mockPokemonService = new PokemonService();
const mockGetByName = jest.spyOn(mockPokemonService, "getByName")
  .mockResolvedValue({ a: "as" });

// 注入mock实例到EvolutionService
const evolutionService = new EvolutionService(mockPokemonService);

afterEach(() => {
  jest.clearAllMocks();
});

test("good response", async () => {
  await evolutionService.chainByName("eevee");
  expect(mockGetByName).toHaveBeenCalledWith("eevee");
});

方案二:直接Mock整个PokemonService类

不修改业务代码的情况下,用jest.mock直接mock整个类,注意适配箭头函数类型的实例方法:

测试文件/evolution/evolution.service.spec.ts

import EvolutionService from "./evolution.service";
import PokemonService from "../pokemon/pokemon.service";

// Mock整个PokemonService类
jest.mock("../pokemon/pokemon.service", () => {
  return {
    default: jest.fn().mockImplementation(() => {
      return {
        getByName: jest.fn().mockResolvedValue({ a: "as" })
      };
    })
  };
});

const evolutionService = new EvolutionService();
// 获取内部实例的mock方法
const mockGetByName = jest.spyOn((evolutionService as any).pokemonService, "getByName");

afterEach(() => {
  jest.clearAllMocks();
});

test("good response", async () => {
  await evolutionService.chainByName("eevee");
  expect(mockGetByName).toHaveBeenCalledWith("eevee");
});

方案三:修改PokemonService方法定义

将箭头函数改为原型方法,适配常规jest.spyOn用法:

修改/pokemon/pokemon.service.ts

import ApiInterface from "../utils/api";

export default class PokemonService {
  private readonly apiInteface: ApiInterface;
  constructor() {
    this.apiInteface = new ApiInterface();
  }

  // 改为原型方法,移除箭头函数
  public getByName<T>(name: string): Promise<T> {
    return this.apiInteface.get(`pokemon/${name}`);
  }
}

修改测试文件/evolution/evolution.service.spec.ts

import EvolutionService from "./evolution.service";
import PokemonService from "../pokemon/pokemon.service";

// Spy原型上的方法
jest.spyOn(PokemonService.prototype, "getByName")
  .mockResolvedValue({ a: "as" });

const evolutionService = new EvolutionService();

afterEach(() => {
  jest.clearAllMocks();
});

test("good response", async () => {
  await evolutionService.chainByName("eevee");
  expect(PokemonService.prototype.getByName).toHaveBeenCalledWith("eevee");
});

内容的提问来源于stack exchange,提问作者Arnab

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 14:13:12