使用 Jest 部分模拟 React 模块

Est*_*ask 8 javascript reactjs jestjs babel-jest create-react-app

我试图在导入的 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
});
Run Code Online (Sandbox Code Playgroud)

这里的问题似乎是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');
...
Run Code Online (Sandbox Code Playgroud)

这可能与 create-react-app Jest+Babel 设置有关,因为我无法jest.dontMock使用 vanilla Jest 和require.

为什么静态React导入被嘲笑React2,而其余​​的则不是?里面究竟发生了什么?

如何jest.dontMock修复当前行为以在顶层部分模拟模块?

Mat*_*tta 4

默认导入:

React.lazy一个简单的解决方案是在以下位置进行模拟setupTest.js

import React from 'react';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });

jest.spyOn(React.lazy);
Run Code Online (Sandbox Code Playgroud)

每个测试文件的任何后续require/imports部分都react将被部分模拟。

工作示例: 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

根/__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);
  });
});
Run Code Online (Sandbox Code Playgroud)

页面/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);
  });
});
Run Code Online (Sandbox Code Playgroud)

命名导入:

工作示例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');
Run Code Online (Sandbox Code Playgroud)

utils/setup/setupTest.js(可选地,您可以将模拟react文件添加为global玩笑函数,这样您就不必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;
Run Code Online (Sandbox Code Playgroud)

根/__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);
  });
});
Run Code Online (Sandbox Code Playgroud)