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

使用jest mock函数无法覆写Hyperledger Fabric接口测试的invoke方法

问题核心原因

你当前的写法不生效的核心是函数引用被提前缓存:

  1. 你的服务端路由/控制器代码中,大概率是提前解构了invoke方法(比如const { invoke } = require('./contracts.srv')),文件加载时就把原始函数的引用存在了本地变量中,后续你修改contractsSrv对象上的invoke属性,无法影响已经被拿走的独立引用。
  2. 测试代码的加载顺序错误:你先在beforeEach中加载了server,完成了整个服务的初始化、所有依赖模块的加载,之后才在it块中修改invoke方法,此时修改已经不会对已经初始化完成的服务产生任何影响。

解决方案

方案1:调整业务代码调用方式 + 调整测试加载顺序

如果可以修改业务代码,优先用这个方案,逻辑更灵活,支持单用例自定义mock返回值。

  1. 首先修改控制器/路由中调用invoke的写法,不要提前解构方法,每次调用都从contractsSrv对象上取最新属性:
// 原错误写法(提前缓存了函数引用)
// const { invoke } = require('../services/contracts.srv');
// 调整为
const contractsSrv = require('../services/contracts.srv');
// 调用时写为 contractsSrv.invoke(参数)
  1. 调整测试代码的执行顺序,mock操作放在server加载之前,每次测试后清除模块缓存避免交叉影响:
'use strict';

require('dotenv').config();
const request = require('supertest');
const contractsSrv = require('../../../services/contracts.srv');
const config = require('config');

let server;
let invokeSpy;
jest.setTimeout(3000);

describe('/api/contracts', () => {
  beforeEach(() => {
    // 先mock方法,再加载server
    invokeSpy = jest.spyOn(contractsSrv, 'invoke').mockResolvedValue({
      data: {
        response: 'true',
        txID: config.get('TX_ID'),
      },
    });
    server = require('../../../server');
  });
  afterEach(() => {
    server.close();
    // 恢复原始方法,清除模块缓存
    invokeSpy.mockRestore();
    jest.resetModules();
  });

  describe('post /', () => {
    it('return transaction record and id', async () => {
      const res = await request(server)
        .post('/api/contracts')
        .send({
          field1: '001',
          field2: '002',
        })
        .set('api-token', config.get('TOKEN'));

      expect(res.status).toBe(200);
      // 可新增断言验证invoke被正确调用
      expect(invokeSpy).toHaveBeenCalledWith(expect.objectContaining({
        field1: '001',
        field2: '002'
      }));
    });
  });
});

方案2:顶部提前mock整个模块

如果不想修改业务代码,可以用jest.mock在文件最顶部提前mock模块,Jest会把jest.mock的执行逻辑提升到所有代码运行之前,保证模块第一次被加载时就是mock后的版本,不受业务代码解构的影响:

'use strict';

// 必须放在所有require语句之前
jest.mock('../../../services/contracts.srv', () => ({
  ...jest.requireActual('../../../services/contracts.srv'),
  invoke: jest.fn().mockResolvedValue({
    data: {
      response: 'true',
      txID: require('config').get('TX_ID'),
    },
  })
}));

require('dotenv').config();
const request = require('supertest');
const contractsSrv = require('../../../services/contracts.srv');
const config = require('config');

let server;
jest.setTimeout(3000);

describe('/api/contracts', () => {
  beforeEach(() => {
    server = require('../../../server');
  });
  afterEach(() => {
    server.close();
    jest.resetAllMocks();
  });

  describe('post /', () => {
    it('return transaction record and id', async () => {
      const res = await request(server)
        .post('/api/contracts')
        .send({
          field1: '001',
          field2: '002',
        })
        .set('api-token', config.get('TOKEN'));

      expect(res.status).toBe(200);
      expect(contractsSrv.invoke).toHaveBeenCalled();
    });
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 11:45:02