Testing React: Target Container is not a DOM element

柔情痞子 提交于 2019-11-30 16:27:35

问题


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?


回答1:


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.




回答2:


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!




回答3:


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();



回答4:


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



来源:https://stackoverflow.com/questions/39986178/testing-react-target-container-is-not-a-dom-element

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!