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

使用Chai与Chai-Spies在同文件中Mock函数测试依赖函数

How to Mock an Async Function to Test Another Async Function in the Same Module

I've defined two async functions (test and test2) in the same file, where test2 calls test. I want to mock the test function to test the business logic of test2. Here's my code:

async function test(data) {
  try {
    const result = await access_to_data_base(data);
    return result;
  } catch (e) {
    return e;
  }
}
async function test2(data) {
  try {
    const testResult = await test(data);
    // 处理获取到的数据
    const result = treated_data();
    return result;
  } catch (e) {
    return e;
  }
}
module.exports = { test, test2 }

How can I achieve this testing requirement?

Great question! The tricky part here is that test2 calls the internal test function directly, not the exported version—so just mocking the exported test won't work out of the box. Let's walk through a few reliable solutions using common Node.js testing tools like Jest and Rewire.


Solution 1: Adjust Your Module to Use Exported References

The simplest fix (if you're open to modifying your source code) is to have test2 call the exported test function instead of the internal one. This makes mocking straightforward since we can override the exported value:

Updated Source Code

async function test(data) {
  try {
    const result = await access_to_data_base(data);
    return result;
  } catch (e) {
    return e;
  }
}
async function test2(data) {
  try {
    // Use module.exports.test instead of the internal test
    const testResult = await module.exports.test(data);
    const result = treated_data();
    return result;
  } catch (e) {
    return e;
  }
}
module.exports = { test, test2 }

Jest Test Code

const { test, test2 } = require('./your-module-file');

describe('test2', () => {
  // Clear mock between tests
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('processes data correctly when test succeeds', async () => {
    // Mock the exported test function to resolve with dummy data
    jest.spyOn(module.exports, 'test').mockResolvedValue('mocked DB result');
    
    // If you need to mock treated_data too, export it first and spy on it
    const treatedDataMock = jest.spyOn(module.exports, 'treated_data').mockReturnValue('processed output');

    const result = await test2('sample input');

    // Verify test was called with the right argument
    expect(test).toHaveBeenCalledTimes(1);
    expect(test).toHaveBeenCalledWith('sample input');
    // Verify the processing logic ran and returned the expected value
    expect(treatedDataMock).toHaveBeenCalledTimes(1);
    expect(result).toBe('processed output');

    // Clean up the mock for treated_data
    treatedDataMock.mockRestore();
  });

  it('returns errors when test fails', async () => {
    const mockError = new Error('DB connection failed');
    jest.spyOn(module.exports, 'test').mockRejectedValue(mockError);

    const result = await test2('bad input');

    expect(test).toHaveBeenCalledWith('bad input');
    expect(result).toEqual(mockError);
  });
});

Solution 2: Use Rewire to Modify Internal Functions (No Source Code Changes)

If you don't want to adjust your source code, use the rewire package to access and override the internal test function directly.

Step 1: Install Rewire

npm install rewire --save-dev

Step 2: Write the Test with Rewire + Sinon (or Jest)

Here's an example using Sinon for stubbing (works similarly with Jest):

const rewire = require('rewire');
const sinon = require('sinon');
const yourModule = rewire('./your-module-file');

describe('test2', () => {
  let testStub;

  // Set up the mock before each test
  beforeEach(() => {
    testStub = sinon.stub();
    // Replace the internal test function with our stub
    yourModule.__set__('test', testStub);
  });

  // Restore the original function after each test
  afterEach(() => {
    yourModule.__restore__();
  });

  it('calls test and processes the result', async () => {
    // Mock test to resolve with dummy data
    testStub.resolves('mocked DB response');
    // Mock the internal treated_data function
    const treatedDataStub = sinon.stub().returns('final processed data');
    yourModule.__set__('treated_data', treatedDataStub);

    const result = await yourModule.test2('test data');

    // Verify test was called correctly
    sinon.assert.calledOnce(testStub);
    sinon.assert.calledWith(testStub, 'test data');
    // Verify processing ran
    sinon.assert.calledOnce(treatedDataStub);
    expect(result).toBe('final processed data');
  });

  it('handles test failures gracefully', async () => {
    const testError = new Error('Query failed');
    testStub.rejects(testError);

    const result = await yourModule.test2('bad data');

    sinon.assert.calledOnce(testStub);
    expect(result).toEqual(testError);
  });
});

Solution 3: Extract test to a Separate Module

For long-term maintainability, consider moving the test function to its own module. This makes mocking trivial since you can just mock the entire module using Jest or any testing framework:

File 1: test.js

async function test(data) {
  try {
    const result = await access_to_data_base(data);
    return result;
  } catch (e) {
    return e;
  }
}
module.exports = test;

File 2: test2.js

const test = require('./test');

async function test2(data) {
  try {
    const testResult = await test(data);
    const result = treated_data();
    return result;
  } catch (e) {
    return e;
  }
}
module.exports = test2;

Jest Test Code

const test2 = require('./test2');
const test = require('./test');

// Mock the entire test module
jest.mock('./test');

describe('test2', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('processes data on test success', async () => {
    test.mockResolvedValue('mocked data');
    const treatedDataMock = jest.spyOn(module.exports, 'treated_data').mockReturnValue('processed');

    const result = await test2('input');

    expect(test).toHaveBeenCalledWith('input');
    expect(result).toBe('processed');
  });
});

Each approach has its tradeoffs: adjusting your source code is simple, Rewire avoids code changes, and extracting to a module improves testability long-term. Pick the one that fits your project's needs!

内容的提问来源于stack exchange,提问作者Soufiane AIT AKKACHE

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:13:55