Skip to content

Latest commit

 

History

History
61 lines (45 loc) · 2.65 KB

File metadata and controls

61 lines (45 loc) · 2.65 KB
id title
bypassing-module-mocks
绕过模块模拟

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

考虑为如下的 createUser 函数编写测试用例:

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

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

在你所编写的测试中,你将会模拟 fetch 函数,这样即可确保它在没有实际发出网络请求的情况下被调用。但是,你还需要模拟fetch的返回值,其包含Response(包装在Promise中),因为后面的函数使用它来获取所创建的用户的ID。因此,你可以先尝试编写这样的测试:

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('http://website.com/users', {
    method: 'POST',
  });
  expect(userId).toBe('4');
});

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. 然而,如果运行如上测试,你会发现createUser函数将失败,并抛出错误信息TypeError: response.text is not a function。这是因为从node-fetch导入的Response类已被模拟(由于jest.mock在测试文件的顶部调用),这样它就不再按照它规定的方式运行了。

为了解决这样的问题,Jest提供了一个jest.requireActual辅助工具。要使上述测试正常工作,需对测试文件中的导入进行以下更改:

// 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 对象,而不是模拟版本。这意味着测试将正确通过。