42

I'm attempting to test a React component with Jest/Enzyme while using Webpack.

I have a very simple test @

import React from 'react';
import { shallow } from 'enzyme';

import App from './App';

it('App', () => {
  const app = shallow(<App />);
  expect(1).toEqual(1);
});

The relative component it's picking up is :

import React, { Component } from 'react';
import { render } from 'react-dom';

// import './styles/normalize.css';

class App extends Component {
  render() {
    return (
      <div>app</div>
    );
  }
}

render(<App />, document.getElementById('app'));

However, running jest causes a failure:

Invariant Violation: _registerComponent(...): Target container is not a DOM element.

With errors @

at Object.<anonymous> (src/App.js:14:48) at Object.<anonymous> (src/App.test.js:4:38)

The test files references line 4, which is the import of <App />, that causes a fail. The stack trace says line 14 of App.js is the reason for the failure -- which is nothing more than the render call from react-dom, something I've never had a challenge with (the app renders properly from my Webpack setup).

For those interested (Webpack code):

module.exports = {
  entry: './src/App',
  output: {
    filename: 'bundle.js',
    path: './dist'
  },
  module: {
    loaders: [
      {
        test: /\.js?$/,
        exclude: /node_modules/,
        loader: 'babel',
        query: {
          presets: ['react', 'es2015']
        }
      },
      {
        test: /\.css$/,
        loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
      },
      {
        test: /\.scss$/,
        loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!sass'
      }
    ]
  }
}

And my package.json:

{
  "name": "tic-tac-dux",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack-dev-server --devtool eval --progress --colors --inline --hot --content-base dist/",
    "test": "jest"
  },
  "jest": {
    "moduleNameMapper": {
      "^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "^.+\\.(css|sass)$": "<rootDir>/__mocks__/styleMock.js"
    }
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.17.0",
    "babel-jest": "^16.0.0",
    "babel-loader": "^6.2.5",
    "babel-polyfill": "^6.16.0",
    "babel-preset-es2015": "^6.16.0",
    "babel-preset-react": "^6.16.0",
    "css-loader": "^0.25.0",
    "enzyme": "^2.4.1",
    "jest": "^16.0.1",
    "jest-cli": "^16.0.1",
    "node-sass": "^3.10.1",
    "react-addons-test-utils": "^15.3.2",
    "react-dom": "^15.3.2",
    "sass-loader": "^4.0.2",
    "style-loader": "^0.13.1",
    "webpack": "^1.13.2",
    "webpack-dev-server": "^1.16.2"
  },
  "dependencies": {
    "react": "^15.3.2",
    "react-dom": "^15.3.2"
  }
}

Oh, and if anyone is going to say that the div element isn't being loaded before the script, here's my index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>App</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="/bundle.js"></script>
  </body>
</html>

What could be the reason for this peculiar rendering problem? Something to do with a new Jest update to 15.0?

ilrein
  • 3,833
  • 4
  • 31
  • 48

8 Answers8

59

For anyone else that was combing through the internet like I've been - looking for a solution to this when testing with Jest - I will back up the answer by @biphobe by saying Jest will cause this error to occur when you export something inside the same file that is calling ReactDOM.render.

In my case, I was exporting an object within my index.js where I was also calling ReactDOM.render. I removed this export and voila!

Zachary Bennett
  • 932
  • 9
  • 15
  • 7
    Thanks, that was exactly what I was looking for. The explicit mention of calling `ReactDOM.render()` was precisely what I needed to understand the exact issue! – Aaron Jul 18 '17 at 14:52
  • Glad it was helpful! – Zachary Bennett Jul 18 '17 at 15:16
  • im having a similar issue when trying to import actions in my test. Unfortunately removing ReactDom does not help – Jabran Saeed Jul 20 '17 at 08:09
  • 1
    @PraveenSrinivasan I was running in to problems with Jest when I tried to both call ReactDOM.render and export something within the same javascript file. – Zachary Bennett Mar 15 '18 at 17:39
  • Ah yes! Thanks. I added an export of an object to my entry file that uses `ReactDom.render`. But there are no tests for that file yet. – andykenward Sep 05 '18 at 07:04
42

App.jsx is supposed to export the App class and do nothing more, render should be called elsewhere.

If you remove the render call from the App.jsx error should disappear, it pops up because the test environment doesn't supply the DOM with an app id.

biphobe
  • 4,525
  • 3
  • 35
  • 46
  • i got same error:- i am tested with ava .. pls tell me where should i wrong.. export default class App extends Component { render() { return (
    {this.props.children}
    ) } } render(( ), document.getElementById('app'));
    – Bhavik Kheni Jan 25 '17 at 08:29
  • @BhavikKheni Please, create new question and include your complete code along with the error and stack trace there. – biphobe Jan 25 '17 at 11:00
  • 6
    Where should it be called if not if not in App.jsx? – Sam Jun 28 '19 at 13:03
34

As I see, this error arises in many cases and requires different approaches to solve it. My scenario is not the same as the example above, I use redux & router, although I was struggling with the same error. What helped me to solve this problem is to change index.js from:

ReactDOM.render(
  <Provider store={store}>
    <AppRouter />
  </Provider>,
  document.getElementById("root")
);
registerServiceWorker();

to:

ReactDOM.render(
    (<Provider store={store}>
        <AppRouter/>
    </Provider>),
     document.getElementById('root') || document.createElement('div') // for testing purposes
);
registerServiceWorker();
Islam Murtazaev
  • 1,488
  • 2
  • 17
  • 27
12

I found a solution for this error to my use case: Using the same Redux store React is using outside of React.

In trying to export my React's Redux store from index.tsx to be used somewhere else outside of the React application, I was getting the same error while running Jest tests (which make use of Enzyme) in the App.tsx file.

The error

The initial code that didn't work when testing React looked like this.

// index.tsx

import * as React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import { applyMiddleware, compose, createStore } from "redux";
import App from "./components/App";
import { rootReducer } from "./store/reducers";
import { initialState } from "./store/state";

const middlewares = [];

export const store = createStore(
    rootReducer,
    initialState,
    compose(applyMiddleware(...middlewares)),
);

render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById("root"),
);

The solution that worked for me

Separate the Redux store logic into a new file named store.ts, then create a default export (to be used by index.tsx, i.e., the React application) and a non-default export with export const store (to be used from non-React classes), as follows.

// store.ts

import { applyMiddleware, compose, createStore } from "redux";
import logger from "redux-logger";
import { rootReducer } from "./store/reducers";
import { initialState } from "./store/state";

const middlewares = [];

export const store = createStore(
    rootReducer,
    initialState,
    compose(applyMiddleware(...middlewares)),
);

export default store;
// updated index.tsx

import * as React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import App from "./components/App";
import store from "./store";

render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById("root"),
);

Using the Redux store in non-React classes

// MyClass.ts

import { store } from "./store"; // store.ts

export default class MyClass {
  handleClick() {
    store.dispatch({ ...new SomeAction() });
  }
}

The default export

A small note before you go. Here is how to use the default and the non-default exports.

  • default export store; is used with import store from "./store";
  • export const store = ... is used with import { store } from "./store";

Hope this helps!

https://nono.ma/says/solved-invariant-violation-target-container-is-not-a-dom-element

Nono
  • 188
  • 1
  • 7
  • Wouldn't this cause the creation of two stores? – tsteve Nov 07 '19 at 20:39
  • Can't we use `import store from "./store";` in our non-React files? Thus, avoiding the need of non-default export in store.ts – Iv Ov Jan 31 '20 at 18:46
  • Thanks for your question @IvOv. I wrote this a year ago and that seems to be the whole point. I would have to test it again but as far as I can recall accessing the same store than React was accessing wasn't working for me and that's why I had to use this workaround. – Nono Feb 01 '20 at 19:34
  • This solved it for me!! Thank you so much for this solution :) question about this solution, if we import "stores" both in "MyClass.ts" and both in "index.ts" isnt that mean that were calling createStore() twice? Thanks! @Nono – Daniela Jul 02 '20 at 16:07
  • This was it for me. It involved a bit of refactoring to remove the `store` creation from index.tsx, but once I moved that into its own file, and imported from there, the test passed. Why doesn't jest like a store that is exported from index.tsx/js? – Seth Lutske Jun 17 '21 at 17:39
5

Make sure in your test file you have well imported the render component. It should be imported from @testing-library/react not from react-dom:

import { render } from '@testing-library/react';
Carlos Vallejo
  • 3,290
  • 3
  • 9
  • 13
2

Well we cant stop the developers from exporting component from any file and test it in isolation even if it have a react-dom import or usage in it .I mean what's wrong in it . We are not trying to disturb the whole file and test out some pieces of it as long as that is a valid piece of code .

Jest does not have an issue with react-dom , however conceptually they are diff . Jest is supposedly a browserless virtual test environment . React-DOM is a library which does the stitching of virtual DOM to real DOM for react components . So obvious enough we can/should not test it in a normal way . But that is not the discussion for now. we are fine as long as our exported components are testable .

So Lets mock it

I did the mock in the testSetup file configured with "setupFilesAfterEnv" in jest config .

jest.mock("react-dom", () => {
    return ({
        "render": jest.fn()
    })
})

That is pretty much worked for me. My react and react-dom code now happily go together in one file , works in browser and in the testing environment as well .

I have not encountered any issues because of this . If there is any I will be looking into the comment section

Debasish Jena
  • 311
  • 2
  • 10
  • I add this to my `request.test.tsx`, then all my tests in the file passed. But some other test files failed. So I applied @Dani Alcala's answer. And all tests passed now. Note: `request.test.tsx` is an encapsulation of `axios` – Leslie Wu Jul 04 '23 at 07:22
1

This solution worked for me. Just render if the element is there:

const root = document.getElementById('root');
if (root) {
    render(
        <App />,
        root,
    );
}
Dani Alcalà
  • 212
  • 3
  • 5
0

I found out this error can also be thrown when working with Portals in your tests. If you want to skip the error you can either mock Portals or add the Portal container element in your render method:

render (
  <div>
    <TestedComponent />
    <div id="portal" />
  </div>
)
Toni Bardina Comas
  • 1,670
  • 1
  • 5
  • 15