Skip to main content
Version: 29.7

绕过模块模拟

Jest 允许你在测试中模拟整个模块,这对于测试你的代码是否正确调用该模块中的函数非常有用。但是,有时你可能希望在测试文件中使用模拟模块的部分内容,在这种情况下,你希望访问原始实现,而不是模拟版本。

¥Jest allows you to mock out whole modules in your tests, which can be useful for testing if your code is calling functions from that module correctly. However, sometimes you may want to use parts of a mocked module in your test file, in which case you want to access the original implementation, rather than a mocked version.

考虑为此 createUser 函数编写一个测试用例:

¥Consider writing a test case for this createUser function:

createUser.js
import fetch from 'node-fetch';

export const createUser = async () => {
const response = await fetch('https://website.com/users', {method: 'POST'});
const userId = await response.text();
return userId;
};

你的测试将需要模拟 fetch 函数,以便我们可以确保在没有实际发送网络请求的情况下调用它。但是,你还需要使用 Response(封装在 Promise 中)模拟 fetch 的返回值,因为我们的函数使用它来获取创建的用户的 ID。因此,你可能最初尝试编写这样的测试:

¥Your test will want to mock the fetch function so that we can be sure that it gets called without actually making the network request. However, you'll also need to mock the return value of fetch with a Response (wrapped in a Promise), as our function uses it to grab the created user's ID. So you might initially try writing a test like this:

jest.mock('node-fetch');

import fetch, {Response} from 'node-fetch';
import {createUser} from './createUser';

test('createUser calls fetch with the right args and returns the user id', async () => {
fetch.mockReturnValue(Promise.resolve(new Response('4')));

const userId = await createUser();

expect(fetch).toHaveBeenCalledTimes(1);
expect(fetch).toHaveBeenCalledWith('https://website.com/users', {
method: 'POST',
});
expect(userId).toBe('4');
});

但是,如果你运行该测试,你会发现 createUser 函数将失败,并抛出错误:TypeError: response.text is not a function。这是因为你从 node-fetch 导入的 Response 类已被模拟(由于测试文件顶部的 jest.mock 调用),因此它不再按应有的方式运行。

¥However, if you ran that test you would find that the createUser function would fail, throwing the error: TypeError: response.text is not a function. This is because the Response class you've imported from node-fetch has been mocked (due to the jest.mock call at the top of the test file) so it no longer behaves the way it should.

为了解决此类问题,Jest 提供了 jest.requireActual 辅助程序。要使上述测试起作用,请对测试文件中的导入进行以下更改:

¥To get around problems like this, Jest provides the jest.requireActual helper. To make the above test work, make the following change to the imports in the test file:

// BEFORE
jest.mock('node-fetch');
import fetch, {Response} from 'node-fetch';
// AFTER
jest.mock('node-fetch');
import fetch from 'node-fetch';
const {Response} = jest.requireActual('node-fetch');

这允许你的测试文件从 node-fetch 导入实际的 Response 对象,而不是模拟版本。这意味着测试现在将正确通过。

¥This allows your test file to import the actual Response object from node-fetch, rather than a mocked version. This means the test will now pass correctly.