Jest: Invariant Violation: ReactCompositeComponent: injectEnvironment() can only be called once.

Created on 2 Aug 2016  Â·  53Comments  Â·  Source: facebook/jest

Note from Maintainers

The bug is in React, and it will be fixed in React 15.4.0.
Until React 15.4.0 is out, update all React packages to 15.4.0-rc.4 if you want to try it out.


We get this error a lot when trying to test our components.

Not sure where this is coming from.

● Runtime Error
  - Invariant Violation: ReactCompositeComponent: injectEnvironment() can only be called once.
        at invariant (node_modules/fbjs/lib/invariant.js:38:15)
        at Object.ReactComponentEnvironment.injection.injectEnvironment (node_modules/react/lib/ReactComponentEnvironment.js:43:60)
        at Object.<anonymous> (node_modules/react/lib/ReactTestRenderer.js:130:37)
        at Object.<anonymous> (node_modules/react-test-renderer/index.js:4:18)
        at Object.<anonymous> (src/shared/components/burger-menu/__tests__/burger-menu.test.js:3:52)
        at handle (node_modules/worker-farm/lib/child/index.js:41:8)
        at process.<anonymous> (node_modules/worker-farm/lib/child/index.js:47:3)
        at emitTwo (events.js:87:13)
        at process.emit (events.js:172:7)
        at handleMessage (internal/child_process.js:695:10)
        at Pipe.channel.onread (internal/child_process.js:440:11)

Most helpful comment

@countoren

The problem will be fixed in React 15.4.0, but it is not released yet.
I believe this is mentioned in the thread above so feel free to read through it!

All 53 comments

This should be fixed with jest-react-native 14.1.1.

I'm not doing any react-native stuff, and I'm not using jest-react-native. Will it be fixed for normal React too ?

I see, it must be react-dom then. Can you try jest.mock('react/lib/ReactDefaultInjection') and see if that makes it work?

Now it give me this error :

Invariant Violation: getNodeFromInstance: Invalid argument.

Can you share a repository on GitHub that highlights this error? I'm pretty sure we need to either provide better mocking defaults in Jest or figure out a way for React to not completely blow up when react-dom and react-test-renderer are both required. The problem here isn't a Jest issue, it's that react-dom and the react-test-renderer are both rendering targets for React and right now in React you can only load a single rendering target.

So I was wondering, since none of my component import react-dom, why it would cause the issue. And actually the issue come from an HOC I use : https://github.com/d6u/react-container-query which do import react-dom. And every component who throws this error is wrapped in the applyContainerQuery HOC. I cannot really share repo since it's on our private Github repo but just doing a really simple test with a component wrapped in this HOC would throw the error.

facebook/react#7386 references the issue too. The submitter provided a test case: https://github.com/NicolasT/react-test-renderer-and-react-dom-incompatible .

It looks like react-test-renderer calls injectEnvironment, but so does react-dom, which is (in my actual application) imported somewhere within a Material-UI module which is in turn imported by my component code.

I'm also seeing this caused by https://github.com/react-bootstrap/react-bootstrap/blob/master/src/Modal.js#L7 when I used a bootstrap Modal in my code. Running jest.mock('react/lib/ReactDefaultInjection') "fixed" that error, but when I open/display the modal, I see:

  - TypeError: Cannot read property 'monitorScrollValue' of null
        at Object._assign.ensureScrollValueMonitoring (node_modules/react/lib/ReactBrowserEventEmitter.js:310:50)
        at Object.ReactMount._renderNewRootComponent (node_modules/react/lib/ReactMount.js:278:30)
        at Object.ReactMount._renderSubtreeIntoContainer (node_modules/react/lib/ReactMount.js:371:32)
        at Object.ReactMount.renderSubtreeIntoContainer [as unstable_renderSubtreeIntoContainer] (node_modules/react/lib/ReactMount.js:313:23)
        at _renderOverlay (node_modules/react-overlays/lib/Portal.js:84:50)
        at componentDidMount (node_modules/react-overlays/lib/Portal.js:47:10)
        at invokeComponentDidMountWithTimer (node_modules/react/lib/ReactCompositeComponent.js:60:18)
...

I tried avoiding this by adding jest.mock('react/lib/ReactBrowserEventEmitter'); but it doesn't help.

TypeError: Can't add property _hostNode, object is not extensible
        at Object.precacheNode (node_modules/react/lib/ReactDOMComponentTree.js:47:22)
        at Object.ReactMount._mountImageIntoNode (node_modules/react/lib/ReactMount.js:487:29)
        at mountComponentIntoNode (node_modules/react/lib/ReactMount.js:112:14)
        at ReactTestReconcileTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:138:20)
        at batchedMountComponentIntoNode (node_modules/react/lib/ReactMount.js:126:15)
        at Object.ReactDefaultBatchingStrategy.batchedUpdates (node_modules/react/lib/ReactDefaultBatchingStrategy.js:61:7)
        at Object.batchedUpdates (node_modules/react/lib/ReactUpdates.js:98:20)
        at Object.ReactMount._renderNewRootComponent (node_modules/react/lib/ReactMount.js:285:18)
        at Object.ReactMount._renderSubtreeIntoContainer (node_modules/react/lib/ReactMount.js:371:32)
        at Object.ReactMount.renderSubtreeIntoContainer [as unstable_renderSubtreeIntoContainer] (node_modules/react/lib/ReactMount.js:313:23)
        at _renderOverlay (node_modules/react-overlays/lib/Portal.js:84:50)
        at componentDidMount (node_modules/react-overlays/lib/Portal.js:47:10)
...

The problem ultimately stems from https://github.com/react-bootstrap/react-overlays/blob/master/src/Portal.js#L74 (which is line 84 in the above stacktrace) called by https://github.com/react-bootstrap/react-overlays/blob/master/src/Portal.js#L28 (aka line 47)

If I try mocking react/lib/ReactMount, I get:

  - TypeError: Cannot read property 'contains' of undefined
        at node_modules/dom-helpers/query/contains.js:8:19
        at focus (node_modules/react-overlays/lib/Modal.js:446:58)
        at onShow (node_modules/react-overlays/lib/Modal.js:388:10)
        at componentDidMount (node_modules/react-overlays/lib/Modal.js:354:12)
...

Which is from the contains function call at https://github.com/react-bootstrap/react-overlays/blob/master/src/Modal.js#L414

I'm getting this as well, when using Jest on a project that uses React and Material UI.

Here are the only two tests I have:

Link.test.js

import React from 'react';
import renderer from 'react/lib/ReactTestRenderer';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import Link from '../imports/common-ui/components/Link';

function handleClick() {

}

test('renders some text with an onClick handler', () => {
  const component = renderer.create(
    <MuiThemeProvider muiTheme={getMuiTheme()}>
      <Link onClick={handleClick}>Click here</Link>
    </MuiThemeProvider>
  );
  let tree = component.toJSON();

  expect(tree).toMatchSnapshot();
});

ArtistTrackUI.test.js

import React from 'react';
import renderer from 'react/lib/ReactTestRenderer';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import ArtistTrackUI from
  '../imports/common-ui/components/Tracks/ArtistTrackUI';

function userAction() {}

test('renders', () => {
  const component = renderer.create(
    <MuiThemeProvider muiTheme={getMuiTheme()}>
      <ArtistTrackUI
        duration={120}
        name="Some Track"
        onUserAction={userAction}
        playing={false}
        position={20}
      />
    </MuiThemeProvider>
  );
  let tree = component.toJSON();

  expect(tree).toMatchSnapshot();
});

Workaround: By using jest.mock('react/lib/ReactDefaultInjection'); with automock turned off, I'm able to get everything running smoothly for tests which don't actually need react-dom. (We get past the load errors.)

For tests that require react-dom, I've duplicated my setup code but replaced import renderer from 'react-test-renderer'; with import { mount } from 'enzyme'; import jasmineEnzyme from 'jasmine-enzyme'; and then with slight modifications -- renaming renderer.create to mount, changing component.toJSON() to component.html(), etc. I'm able to get things up and running. See Enzyme's Full Render API and jasmine-enzyme, which I loaded using the suggested non-Jest beforeEach filter instead of using the Jest-suggested setupTestFrameworkScriptFile so I could run both render functions in different Jest test files at the same time.

jest.mock('react-dom') should be enough for this.

(I’m not saying it’s good though—this requirement is confusing and should ideally be fixed, or at least documented, on the React side.)

With jest.mock('react-dom') I still get the following error, same as when I mock both react/lib/ReactDefaultInjection and react/lib/ReactMount

  - TypeError: Cannot read property 'contains' of undefined
        at node_modules/dom-helpers/query/contains.js:8:19
        at focus (node_modules/react-overlays/lib/Modal.js:446:58)
        at onShow (node_modules/react-overlays/lib/Modal.js:388:10)
        at componentDidMount (node_modules/react-overlays/lib/Modal.js:354:12)
        at invokeComponentDidMountWithTimer (node_modules/react/lib/ReactCompositeComponent.js:60:18)
...

Edit: Enzyme's mount "just works" except when my code is asking for layout dimensions of obviously unrendered components. Further mocking seems to be required in each case.

With Enzyme's mount, "it just works".

Enzyme’s mount() uses jsdom which is a complete re-implementation of DOM in memory 😄 . I don’t think it’s entirely surprising that it “just works” for this use case compared to a utility that has barely been out for a week, and has explicit goal of _not_ implementing DOM-specific methods.

I still get the following error, same as when I mock both react/lib/ReactDefaultInjection and react/lib/ReactMount

If you look at the stack trace, you will see that it fails because it calls DOM APIs (namely node.contains()). As I suggested in https://github.com/facebook/react/issues/7371#issuecomment-238091530, the best we can do is to provide a mock object that has DOM node APIs but doesn’t actually do anything. This is not currently implemented but your ideas are welcome too!

Oh, and another workaround in the meantime would be to mock the component that uses those DOM APIs (in your case, node_modules/react-overlays/lib/Modal.js).

Thanks @gaearon - I tried the mock route before commenting. I'm not sure what I'm doing wrong, I've tried mocking jest.mock('dom-helpers');, jest.setMock('dom-helpers/util/inDOM', false); and creating a top-level __mocks__ folder to mock Modal's focus function to a noop. But every time it keeps loading ./node_modules/dom-helpers/query/contains.js with inDOM returning true, so my mocks aren't taking effect. Very strange, because jest.mock('react-dom'); worked, as did a mock for a local file. I wonder if it's somehow pre-loaded, or if a hook gets lost somewhere. When I place a breakpoint in dom-helpers/query/contains.js, I can run jest.setMock in the REPL and it works exactly as expected, with a require before returning true for inDOM before and false afterward. Any suggestions for debugging requires-mocking?

I was able to get it working thanks to extensive use of a debugger and conditional breakpoints, with the following line. No idea why it wasn't working before, maybe it's because I'm using ES6 function shorthand, or maybe it was the lack of .js at the end in my last comment's examples.

jest.mock('dom-helpers/util/inDOM.js', ()=>false);

As to the rest, it appears I'm using too much state and not enough props for me to interact with the shallow rendered output of toJSON(), if I understand this correctly.

Hi @cpojer 's solution works for me
jest.mock('react/lib/ReactDefaultInjection');
but, I wonder why I get this error if I don't use ReactDOM or enzyme explicitly. I have very similar test as in the CRA and I don't see any mocking there. IS there a way to trace where all the registration happening.

I wonder why I get this error if I don't use ReactDOM or enzyme explicitly.

Some component you use imports react-dom (e.g. for findDOMNode).

Yeah we should really come up with documentation for this and maybe even add error messages to React that point you to what you need to do to avoid the errors.

I just ran into the same issue, and a search brought me to @cpojer's jest.mock('react/lib/ReactDefaultInjection'); solution which worked for me as well

There is a workaround and the next version of React won't have this problem.

(For future readers: probably not 15.3.1 but 15.4.0 might have this fix.)

confirming 15.3.1 still has this problem

Hey all, keep in mind that mocking an internal React module can have unexpected consequences. See https://github.com/facebook/react/issues/7601.

I ran into the same issue. Tried using jest.mock('react/lib/ReactDefaultInjection'); but it throws a new error saying:

Invariant Violation: getNodeFromInstance: Invalid argument.

and stack trace:

        at invariant (node_modules/fbjs/lib/invariant.js:38:15)
        at Object.getNodeFromInstance (node_modules/react/lib/ReactDOMComponentTree.js:156:77)
        at Object.findDOMNode (node_modules/react/lib/findDOMNode.js:50:41)
        at Constructor.transition (node_modules/react/lib/ReactCSSTransitionGroupChild.js:53:25)
        at Constructor.componentWillAppear (node_modules/react/lib/ReactCSSTransitionGroupChild.js:140:12)
        at Constructor.performAppear (node_modules/react/lib/ReactTransitionGroup.js:112:17)
        at Constructor.componentDidMount (node_modules/react/lib/ReactTransitionGroup.js:59:14)
        at ReactCompositeComponentWrapper.invokeComponentDidMountWithTimer (node_modules/react/lib/ReactCompositeComponent.js:60:18)

@iam-peekay we do not recommend mocking any internal module for React. It's very likely doing so will cause strange issues like what you're seeing. This should be resolved in 15.4.0 though!

👍

Having this error when using mount: Cannot read property 'monitorScrollValue' of null.
Tried @LouisStAmour's solution but I can't make it work :/

Hey @Aweary, if mocking is not recommended, what's the recommendation since 15.4 is not released yet?

@tleunen the easiest way is to keep tests that utilize packages dependant on react-dom in a separate file from tests that use react-test-renderer. Not ideal, but it should avoid this for now.

Otherwise, @cpojer I think you once mentioned using jest.resetModuleRegistry to get this working in one file? Not sure if you recommend actually doing that though.

@Aweary I see a new minor release for react in just few mins back. as mentioned here https://github.com/facebook/react/issues/7601#issuecomment-247639258, I wonder whether the fix is already in the master or how we know it will be fixed in 15.4. I see all these issues are closed, but not referring to the fix. thanks.

@bsr203 I don't think the fix is being released as a patch, so it wasn't included AFAIK. It should be out with the next minor release, which would be 15.4

A workaround to use both enzyme (with react-dom) and react-test-renderer in one file is:

beforeEach(() => jest.resetModules());

test('react-dom', () => {
  const React = require('react');
  const enzyme = require('enzyme');
  // write your test with enzyme
});

test('react-test-renderer', () => {
  const React = require('react');
  const renderer = require('react-test-renderer');
  // write your test with react-test-renderer
});

This will give you fresh copies of React for every test.

Thanks @cpojer, I'll try that.

What if, inside the same test, we want to render something with Enzyme, and then use rest-test-renderer for the snapshot? (I know we'll do 2 renders but the snapshots makes it a lot easier to test/review).

const wrapper = mount(
    <CustomComponent />
);
wrapper.find('.something').simulate('click');

const component = renderer.create(wrapper.node);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();

Is this something that will be supported in the future?

I don't see the value in that. Once the test-renderer has a find API there is little value in using enzyme for this kind of testing. The React team is working on it.

But the test-renderer doesn't have any way to simulate a click, the only way is to call the props. But what if a component is handling a click on an element to update the internal state and change the rendering based on that?
Unless you know they're also working on a way to click on something? :)

What is a click in an environment that is a fake implementation of the DOM? It is simply a function call, so calling a prop called onClick is exactly the same.

If anyone else is trying to do Jest snapshots and enzyme tests in the same file, I started using enzyme-to-json as an alternative to react-test-renderer and it's been working great. (@tleunen)

just cloned a fresh copy, after npm i running npm test got
injectEnvironment() can only be called once.
in all competents tests
from the ReactTestRenderer import sentences .
Any help?

@countoren

The problem will be fixed in React 15.4.0, but it is not released yet.
I believe this is mentioned in the thread above so feel free to read through it!

@shanecav thanks for your suggestion!

just in case anyone is blocked by this issue. enzyme-to-json as an alternative to react-test-renderer works like a charm!

You should be able to try React 15.4.0-rc.4 (be sure to bump all packages).
If you have issues please report them in https://github.com/facebook/react/issues/7770.

Using 17.0.0 and still got the error. Switched over to enzyme-to-json and it's working fine.

@DarrylD

Have you had a chance to read my comments above?

From https://github.com/facebook/jest/issues/1353#issuecomment-254761524:

You should be able to try React 15.4.0-rc.4 (be sure to bump all packages).

From https://github.com/facebook/jest/issues/1353#issuecomment-249906262:

The problem will be fixed in React 15.4.0, but it is not released yet.

It is not expected to be fixed in Jest 17 because it is not a bug in Jest.

The bug is in React, and it will be fixed in React 15.4.0.
You can already install React 15.4.0-rc.4 if you want to try it out.

Cheers!

@gaearon Skimmed over the most important comment 😑. Sorry about that.

No worries. Let me add this to the top post so less people miss it.

After updating to react 15.4.0-rc.4 I get this when I run my test:

 Cannot find module 'react/lib/ReactTestRenderer' from 'index.js'

      at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:142:17)
      at Object.<anonymous> (node_modules/react-test-renderer/index.js:4:18)
      at Object.<anonymous> (__test__/src/components/article/article.test.js:2:52)

My test is quite simple:

import React from 'react';
import renderer from 'react-test-renderer';
import Article from '../../../../src/components/article';


describe('article', () => {

  it('renders', () => {
    const tree = renderer.create(
      <Article
        hasPadding={true}
        newsId={1} />
    ).toJSON();

    expect(tree).toMatchSnapshot();
  });
});

@matheus208 you need to use the new version of react-test-renderer too (set as next tag like React).

This was fixed in React 15.4.0 which is out today.

Hey @gaearon. I'm facing this same issue when I try to mount a component using react 15.6. I'm getting the message:

    console.error node_modules/fbjs/lib/warning.js:36
      Warning: Unknown props `mobile`, `precision` on <Test> tag. Remove these props from the element. For details, see https://fb.me/react-unknown-prop
          in Test (at index.js:21)
          in div (at index.js:18)
          in NumericInput (created by WrapperComponent)
          in WrapperComponent

When I run the mount on the following test:

import React from 'react';
import renderer from 'react-test-renderer';
import { mount, shallow } from 'enzyme';
import NumericInput from '../index';

jest.mock('react-numeric-input', () => 'Test');

describe('NumericInput', () => {
  it('renders without crashing', () => {
    mount(<NumericInput isMax />);
  });
});

with the component:

import PropTypes from 'prop-types';
import React from 'react';
import cx from 'classnames';
import ReactNumericInput from 'react-numeric-input';

import styles from './NumericInput.css';

const propTypes = {
  value: PropTypes.number,
  isMax: PropTypes.bool.isRequired,
};

const defaultProps = {
  value: 0,
};

const NumericInput = ({ value, isMax }) => (
  <div>
    <span className={cx(styles.label)} > {isMax ? 'Max' : 'Min'} </span>

    <ReactNumericInput
      className={cx(styles['numeric-input'])}
      mobile={false}
      value={value}
      format={num => (`${num} RSF`)}
      precision={3}
      size={10}
    />
  </div>
);

NumericInput.propTypes = propTypes;
NumericInput.defaultProps = defaultProps;

export default NumericInput;

React version: "react": "^15.6.1"
Jest: "jest": "^20.0.4"
Enzyme: "enzyme": "^2.9.1",

@kaiomagalhaes Why is this the same issue? The message in https://github.com/facebook/jest/issues/1353#issue-168871995 looks very different to me.

In your case I think using jest.mock('react-numeric-input', () => 'react-numeric-input'); could work around the issue since it wouldn’t check for valid DOM nodes on what appears to be a custom component. In React 16 we’ll likely remove this warning anyway so it shouldn’t be a problem.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hramos picture hramos  Â·  3Comments

ticky picture ticky  Â·  3Comments

ianp picture ianp  Â·  3Comments

kgowru picture kgowru  Â·  3Comments

jardakotesovec picture jardakotesovec  Â·  3Comments