web-dev-qa-db-fra.com

Test de React: le conteneur cible n'est pas un élément DOM

J'essaie de tester un composant React avec Jest/Enzyme lors de l'utilisation de Webpack.

J'ai un test très simple @

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

import App from './App';

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

Le composant relatif qu'il prend est:

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

Cependant, l'exécution de jest provoque un échec:

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

Avec des erreurs @

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

Les fichiers de test font référence à la ligne 4, qui est l'importation de <App />, Qui provoque un échec. La trace de la pile indique que la ligne 14 de App.js Est la raison de l'échec - qui n'est rien de plus que l'appel de rendu de react-dom, Quelque chose que je n'ai jamais eu de problème avec (l'application rend correctement à partir de ma configuration Webpack).

Pour les personnes intéressées (code Webpack):

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'
      }
    ]
  }
}

Et mon 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, et si quelqu'un veut dire que l'élément div n'est pas chargé avant le script, voici mon 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>

Quelle pourrait être la raison de ce problème de rendu particulier? Quelque chose à voir avec une nouvelle mise à jour Jest vers 15.0?

19
ilrein

App.jsx est censé exporter la classe App et ne rien faire de plus, render doit être appelé ailleurs.

Si vous supprimez l'appel render de l'erreur App.jsx devrait disparaître, il apparaît car l'environnement de test ne fournit pas au DOM un identifiant app.

27
biphobe

Pour toute autre personne qui peignait sur Internet comme je l'ai été - à la recherche d'une solution à cela lors des tests avec Jest - je sauvegarderai la réponse par @biphobe en disant que Jest provoquera cette erreur lorsque vous exporterez quelque chose à l'intérieur du même fichier qui appelle ReactDOM.render.

Dans mon cas, j'exportais un objet dans mon index.js où j'appelais également ReactDOM.render. J'ai supprimé cette exportation et le tour est joué!

23
Zachary Bennett

Comme je le vois, cette erreur survient dans de nombreux cas et nécessite différentes approches pour la résoudre. Mon scénario n'est pas le même que l'exemple ci-dessus, j'utilise redux & router, même si je luttais avec la même erreur. Ce qui m'a aidé à résoudre ce problème, c'est de changer index.js de:

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

à:

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

J'ai trouvé une solution à cette erreur dans mon cas d'utilisation: en utilisant le même magasin Redux React utilise en dehors de React.

En essayant d'exporter le Redux de mon React store de index.tsx à utiliser ailleurs qu'en dehors de l'application React, j'obtenais la même erreur lors de l'exécution des tests Jest (qui utilisent Enzyme) dans le App.tsx fichier.

L'erreur

Le code initial qui ne fonctionnait pas lors des tests React ressemblait à ceci.

// 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"),
);

La solution qui a fonctionné pour moi

Séparez la logique du magasin Redux dans un nouveau fichier nommé store.ts, puis créez un default export (à utiliser par index.tsx, c'est-à-dire l'application React) et une exportation non par défaut avec export const store (à utiliser à partir des classes non React), comme suit.

// 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"),
);

Utilisation du magasin Redux dans des classes non React

// MyClass.ts

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

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

L'exportation default

Une petite note avant de partir. Voici comment utiliser le default et les exportations non par défaut.

  • default export store; est utilisé avec import store from "./store";
  • export const store = ... est utilisé avec import { store } from "./store";

J'espère que cela t'aides!

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

2
Nono