使用 Jest 部分模拟 React 模块
Partially mock React module with Jest
我试图在导入的 React 模块中只模拟一个函数,保持模块的其余部分未被模拟,并在所有测试的顶层执行此操作。
我正在使用新的 create-react-app 项目和一个测试来观察问题。
重现步骤:
create-react-app test
- 使用提供的
src/App.test.js
作为唯一的测试文件
npm run test
App.test.js
jest.mock('react', () => {
jest.dontMock('react');
const React = require('react');
const lazy = jest.fn();
return {
...React,
lazy
};
});
import * as React from 'react';
const React2 = require('react');
it('should partially mock React module', async () => {
expect(jest.isMockFunction(React.lazy)).toBe(true); // passes
expect(jest.isMockFunction(React2.lazy)).toBe(true); // fails
expect(jest.isMockFunction(require('react').lazy)).toBe(true); // fails
expect(jest.isMockFunction((await import('react')).lazy)).toBe(true); // fails
});
这里的问题似乎是 jest.dontMock
因为它阻止了 require
和动态 import
被模拟,但目前还不清楚为什么可以模拟静态 import
这样,因为它以任何方式使用 require
。这是转译后的文件:
"use strict";
jest.mock('react', () => {
jest.dontMock('react');
const React = require('react');
const lazy = jest.fn();
return (0, _objectSpread2.default)({}, React, {
lazy
});
});
var _interopRequireWildcard3 = require("...\node_modules\@babel\runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("...\node_modules\@babel\runtime/helpers/interopRequireDefault");
var _interopRequireWildcard2 = _interopRequireDefault(require("...\node_modules\@babel\runtime/helpers/interopRequireWildcard"));
var _objectSpread2 = _interopRequireDefault(require("...\node_modules\@babel\runtime/helpers/objectSpread"));
var React = _interopRequireWildcard3(require("react"));
const React2 = require('react');
...
这可能与 create-react-app Jest+Babel 设置有关,因为我无法使 jest.dontMock
与 vanilla Jest 和 require
.
一起工作不正确
为什么 static React
import 被嘲笑了,而 React2
而其他的却没有?里面到底发生了什么?
如何 jest.dontMock
修复当前行为以在顶层部分模拟模块?
默认导入:
一个简单的解决方案是在 setupTest.js
:
中模拟 React.lazy
import React from 'react';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
jest.spyOn(React.lazy);
react
的任何后续 require/imports
将针对每个测试文件进行部分模拟。
工作示例:https://github.com/mattcarlotta/react-lazy-mocked(我不使用 create-react-app
,但 jest
可以按照与我有)
安装:
git clone git@github.com:mattcarlotta/react-lazy-mocked.git
cd react-lazy-mocked
yarn install
yarn test
root/__tests__/root.test.js
import React from 'react';
import App from '../index.js';
const React2 = require('react');
describe('App', () => {
const wrapper = mount(<App />);
it('renders without errors', () => {
const homeComponent = wrapper.find('.app');
expect(homeComponent).toHaveLength(1);
});
it('should partially mock React module', async () => {
expect(jest.isMockFunction(await require('react').lazy)).toBe(true); // eslint-disable-line global-require
expect(jest.isMockFunction(React)).toBe(false);
expect(jest.isMockFunction(React.lazy)).toBe(true);
expect(jest.isMockFunction(React2)).toBe(false);
expect(jest.isMockFunction(React2.lazy)).toBe(true);
});
it('should no longer be partially mocked within the test file', () => {
React.lazy.mockRestore();
expect(jest.isMockFunction(React.lazy)).toBe(false);
});
});
pages/Home/__tests__/Home.test.js
import React from 'react';
import Home from '../index.js';
describe('Home', () => {
const wrapper = shallow(<Home />);
it('renders without errors', () => {
const homeComponent = wrapper.find('.app');
expect(homeComponent).toHaveLength(1);
});
it('should partially mock React module', async () => {
expect(jest.isMockFunction(React.lazy)).toBe(true);
});
});
命名导入:
工作示例:https://github.com/mattcarlotta/named-react-lazy-mocked
安装:
git clone git@github.com:mattcarlotta/named-react-lazy-mocked.git
cd named-react-lazy-mocked
yarn install
yarn test
utils/__mocks__/react.js
jest.mock('react', () => ({
...require.requireActual('react'),
lazy: jest.fn(),
}));
module.exports = require.requireMock('react');
utils/setup/setupTest.js(可选,您可以将模拟的 react
文件添加为 global
jest 函数,这样您就不会必须为每个测试写 import * as React from 'react'
):
import { JSDOM } from 'jsdom';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// import React from '../__mocks__/react';
configure({ adapter: new Adapter() });
// global.React = React;
root/__tests__/root.test.js
import * as React from 'react';
import App from '../index.js';
const React2 = require('react');
describe('App', () => {
const wrapper = mount(<App />);
it('renders without errors', () => {
const homeComponent = wrapper.find('.app');
expect(homeComponent).toHaveLength(1);
});
it('should partially mock React module', async () => {
expect(jest.isMockFunction(await require('react').lazy)).toBe(true); // eslint-disable-line global-require
expect(jest.isMockFunction(React)).toBe(false);
expect(jest.isMockFunction(React.lazy)).toBe(true);
expect(jest.isMockFunction(React2)).toBe(false);
expect(jest.isMockFunction(React2.lazy)).toBe(true);
});
});
我试图在导入的 React 模块中只模拟一个函数,保持模块的其余部分未被模拟,并在所有测试的顶层执行此操作。
我正在使用新的 create-react-app 项目和一个测试来观察问题。
重现步骤:
create-react-app test
- 使用提供的
src/App.test.js
作为唯一的测试文件 npm run test
App.test.js
jest.mock('react', () => {
jest.dontMock('react');
const React = require('react');
const lazy = jest.fn();
return {
...React,
lazy
};
});
import * as React from 'react';
const React2 = require('react');
it('should partially mock React module', async () => {
expect(jest.isMockFunction(React.lazy)).toBe(true); // passes
expect(jest.isMockFunction(React2.lazy)).toBe(true); // fails
expect(jest.isMockFunction(require('react').lazy)).toBe(true); // fails
expect(jest.isMockFunction((await import('react')).lazy)).toBe(true); // fails
});
这里的问题似乎是 jest.dontMock
因为它阻止了 require
和动态 import
被模拟,但目前还不清楚为什么可以模拟静态 import
这样,因为它以任何方式使用 require
。这是转译后的文件:
"use strict";
jest.mock('react', () => {
jest.dontMock('react');
const React = require('react');
const lazy = jest.fn();
return (0, _objectSpread2.default)({}, React, {
lazy
});
});
var _interopRequireWildcard3 = require("...\node_modules\@babel\runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("...\node_modules\@babel\runtime/helpers/interopRequireDefault");
var _interopRequireWildcard2 = _interopRequireDefault(require("...\node_modules\@babel\runtime/helpers/interopRequireWildcard"));
var _objectSpread2 = _interopRequireDefault(require("...\node_modules\@babel\runtime/helpers/objectSpread"));
var React = _interopRequireWildcard3(require("react"));
const React2 = require('react');
...
这可能与 create-react-app Jest+Babel 设置有关,因为我无法使 jest.dontMock
与 vanilla Jest 和 require
.
为什么 static React
import 被嘲笑了,而 React2
而其他的却没有?里面到底发生了什么?
如何 jest.dontMock
修复当前行为以在顶层部分模拟模块?
默认导入:
一个简单的解决方案是在 setupTest.js
:
React.lazy
import React from 'react';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
jest.spyOn(React.lazy);
react
的任何后续 require/imports
将针对每个测试文件进行部分模拟。
工作示例:https://github.com/mattcarlotta/react-lazy-mocked(我不使用 create-react-app
,但 jest
可以按照与我有)
安装:
git clone git@github.com:mattcarlotta/react-lazy-mocked.git
cd react-lazy-mocked
yarn install
yarn test
root/__tests__/root.test.js
import React from 'react';
import App from '../index.js';
const React2 = require('react');
describe('App', () => {
const wrapper = mount(<App />);
it('renders without errors', () => {
const homeComponent = wrapper.find('.app');
expect(homeComponent).toHaveLength(1);
});
it('should partially mock React module', async () => {
expect(jest.isMockFunction(await require('react').lazy)).toBe(true); // eslint-disable-line global-require
expect(jest.isMockFunction(React)).toBe(false);
expect(jest.isMockFunction(React.lazy)).toBe(true);
expect(jest.isMockFunction(React2)).toBe(false);
expect(jest.isMockFunction(React2.lazy)).toBe(true);
});
it('should no longer be partially mocked within the test file', () => {
React.lazy.mockRestore();
expect(jest.isMockFunction(React.lazy)).toBe(false);
});
});
pages/Home/__tests__/Home.test.js
import React from 'react';
import Home from '../index.js';
describe('Home', () => {
const wrapper = shallow(<Home />);
it('renders without errors', () => {
const homeComponent = wrapper.find('.app');
expect(homeComponent).toHaveLength(1);
});
it('should partially mock React module', async () => {
expect(jest.isMockFunction(React.lazy)).toBe(true);
});
});
命名导入:
工作示例:https://github.com/mattcarlotta/named-react-lazy-mocked
安装:
git clone git@github.com:mattcarlotta/named-react-lazy-mocked.git
cd named-react-lazy-mocked
yarn install
yarn test
utils/__mocks__/react.js
jest.mock('react', () => ({
...require.requireActual('react'),
lazy: jest.fn(),
}));
module.exports = require.requireMock('react');
utils/setup/setupTest.js(可选,您可以将模拟的 react
文件添加为 global
jest 函数,这样您就不会必须为每个测试写 import * as React from 'react'
):
import { JSDOM } from 'jsdom';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// import React from '../__mocks__/react';
configure({ adapter: new Adapter() });
// global.React = React;
root/__tests__/root.test.js
import * as React from 'react';
import App from '../index.js';
const React2 = require('react');
describe('App', () => {
const wrapper = mount(<App />);
it('renders without errors', () => {
const homeComponent = wrapper.find('.app');
expect(homeComponent).toHaveLength(1);
});
it('should partially mock React module', async () => {
expect(jest.isMockFunction(await require('react').lazy)).toBe(true); // eslint-disable-line global-require
expect(jest.isMockFunction(React)).toBe(false);
expect(jest.isMockFunction(React.lazy)).toBe(true);
expect(jest.isMockFunction(React2)).toBe(false);
expect(jest.isMockFunction(React2.lazy)).toBe(true);
});
});