Jest SpyOn无法Mock类方法问题求助
Jest无法Mock类实例方法的问题解决方案
问题根源
- 测试中
spyOn的pokemonService实例,与EvolutionService内部自行创建的PokemonService实例是完全独立的两个对象,mock前者的方法不会作用到后者。 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
相关产品推荐
相关产品推荐

