如何在 Jest 中动态修改模拟类方法的返回值

1次阅读

如何在 Jest 中动态修改模拟类方法的返回值

本文介绍在 Jest 测试中,如何在不重复调用 jest.mock() 的前提下,灵活地为模拟类(es6 class)的方法设置不同返回值,适用于多场景单元测试需求。

本文介绍在 jest 测试中,如何在不重复调用 `jest.mock()` 的前提下,灵活地为模拟类(es6 class)的方法设置不同返回值,适用于多场景单元测试需求。

在 Jest 中,对 ES6 类进行模拟时,若需在不同测试用例中让同一方法返回不同值(例如 methodOne 返回 “ABC” 或 “XYZ”),直接在 jest.mock() 工厂函数中硬编码返回值是不可行的——因为 jest.mock() 会被提升(hoisted)至文件顶部执行,无法按测试用例动态变更。

正确的做法是:先通过 jest.mock(‘./module’) 创建自动模拟(auto-mock),再利用 mockImplementation() 或 mockImplementationOnce() 在具体测试块中动态重写类的构造行为。该方式不仅避免了重复 mock 的语法错误,还支持跨 it() 块独立定制返回逻辑。

✅ 推荐实现方式(基于自动模拟)

假设原始模块 ./some-module.js 定义如下:

export class Abc {   constructor(config) {}    async methodOne(params) {     return {       message: { content: 'This text I need to change' },     };   } }

主逻辑 main.js 调用该类:

import { Abc } from './some-module';  export async function main() {   const abc = new Abc();   const res = await abc.methodOne();   return res.message.content; }

对应测试 main.test.js 应这样编写:

import { main } from './main'; import { Abc } from './some-module';  // ✅ 启用自动模拟(不传工厂函数) jest.mock('./some-module');  describe('动态修改模拟类方法返回值', () => {   test('用例一:methodOne 应返回 "ABC"', async () => {     // ✅ 动态覆盖 Abc 类的实例行为     Abc.mockImplementation(() => ({       methodOne: async () => ({ message: { content: 'ABC' } }),     }));      expect(await main()).toBe('ABC');   });    test('用例二:methodOne 应返回 "XYZ"', async () => {     // ✅ 每个测试可独立配置,互不影响     Abc.mockImplementation(() => ({       methodOne: async () => ({ message: { content: 'XYZ' } }),     }));      expect(await main()).toBe('XYZ');   });    test('进阶:单次生效(仅本次调用)', async () => {     Abc.mockImplementationOnce(() => ({       methodOne: async () => ({ message: { content: 'ONCE' } }),     }));      expect(await main()).toBe('ONCE');     // 后续调用将回退到默认 auto-mock 行为(如返回 undefined 或空对象)   }); });

⚠️ 关键注意事项

  • 不要混用 jest.mock(…, factory) 和 mockImplementation():一旦使用带工厂函数的 jest.mock(),Jest 将锁定该实现,后续 mockImplementation() 不会生效;
  • mockImplementation() 影响所有后续 new Abc() 实例,适合整个 it() 块统一行为;
  • mockImplementationOnce() 更适合链式调用或多次实例化场景中的精准控制
  • 若被测代码中 Abc 是默认导出(export default class Abc {…}),则需通过 Abc.default.mockImplementation(…) 访问;
  • 所有 mockImplementation* 调用必须在 jest.mock() 之后、实际 new Abc() 之前执行。

通过这种模式,你可以在保持测试隔离性的同时,精准控制模拟类的行为,显著提升测试覆盖率与可维护性。

text=ZqhQzanResources