最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Partially mock React module with Jest - Stack Overflow

programmeradmin2浏览0评论

I'm trying to mock only one function in imported React module, keep the rest of the module unmocked and do this at top level for all tests.

I'm using fresh create-react-app project with a single test to observe the problem.

Steps to reproduce:

  • create-react-app test
  • use provided src/App.test.js as the only test file
  • 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
});

The problem here seems to be jest.dontMock as it prevents require and dynamic import from being mocked, but it remains unclear why it was possible to mock static import this way, as it uses require any way. Here's transpiled file:

"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');
...

This may have something to do with create-react-app Jest+Babel setup because I was unable to make jest.dontMock work incorrectly with vanilla Jest and require.

Why is static React import mocked but React2 and the rest aren't? What exactly is going on inside?

How can jest.dontMock current behaviour be fixed to partially mock a module at top level?

I'm trying to mock only one function in imported React module, keep the rest of the module unmocked and do this at top level for all tests.

I'm using fresh create-react-app project with a single test to observe the problem.

Steps to reproduce:

  • create-react-app test
  • use provided src/App.test.js as the only test file
  • 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
});

The problem here seems to be jest.dontMock as it prevents require and dynamic import from being mocked, but it remains unclear why it was possible to mock static import this way, as it uses require any way. Here's transpiled file:

"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');
...

This may have something to do with create-react-app Jest+Babel setup because I was unable to make jest.dontMock work incorrectly with vanilla Jest and require.

Why is static React import mocked but React2 and the rest aren't? What exactly is going on inside?

How can jest.dontMock current behaviour be fixed to partially mock a module at top level?

Share edited Mar 24, 2019 at 20:28 Estus Flask asked Mar 24, 2019 at 18:29 Estus FlaskEstus Flask 224k79 gold badges472 silver badges611 bronze badges 2
  • I think require.requireActual should do the trick as mentioned in this issue. Also jest.dontMock('react') is not needed with this. I don't know why dontMock is behaving differently for import vs require - it should prevent mocking in both cases. – AWolf Commented Mar 24, 2019 at 20:37
  • 1 @AWolf I knew about requireActual but totally forgot about it and somehow overlooked it in the issue you linked. Indeed, that's the solution, thank you. Consider providing this fix as an answer if you wish. – Estus Flask Commented Mar 24, 2019 at 22:24
Add a ment  | 

1 Answer 1

Reset to default 4

Default Imports:

A simple solution would be to mock React.lazy in the 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);

Any subsequent require/imports of react will be partially mocked for each test file.

Working example: https://github./mattcarlotta/react-lazy-mocked (I don't use the create-react-app, but jest can be set up the same way as I have it)

Installation:

  • git clone [email protected]: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);
  });
});

Named Imports:

Working example: https://github./mattcarlotta/named-react-lazy-mocked

Installation:

  • git clone [email protected]: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 (optionally, you can add the mocked react file as a global jest function so you won't have to write import * as React from 'react' for every test):

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);
  });
});
发布评论

评论列表(0)

  1. 暂无评论