#javascript #reactjs #ecmascript-6 #tdd
#javascript #reactjs #ecmascript-6 #tdd
Вопрос:
Я пытаюсь протестировать компонент React с помощью Jest / Enzyme при использовании Webpack.
У меня очень простой тест @
import React from 'react';
import { shallow } from 'enzyme';
import App from './App';
it('App', () => {
const app = shallow(<App />);
expect(1).toEqual(1);
});
Относительный компонент, который он собирает, является :
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'));
Однако запуск jest
приводит к сбою:
Invariant Violation: _registerComponent(...): Target container is not a DOM element.
С ошибками @
at Object.<anonymous> (src/App.js:14:48)
at Object.<anonymous> (src/App.test.js:4:38)
Тестовые файлы ссылаются на строку 4, которая является импортом <App />
, что вызывает сбой. Трассировка стека указывает, что причиной сбоя является строка 14 из App.js
, что является не чем иным, как вызовом рендеринга react-dom
, с чем у меня никогда не было проблем (приложение правильно отображается из моей настройки Webpack).
Для тех, кто заинтересован (код 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?modulesamp;importLoaders=1amp;localIdentName=[name]__[local]___[hash:base64:5]'
},
{
test: /.scss$/,
loader: 'style!css-loader?modulesamp;importLoaders=1amp;localIdentName=[name]__[local]___[hash:base64:5]!sass'
}
]
}
}
И мой 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"
}
}
О, и если кто-нибудь собирается сказать, что элемент div не загружается перед скриптом, вот мой 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>
В чем может быть причина этой специфической проблемы с рендерингом? Что-то связано с новым обновлением Jest до 15.0?
Ответ №1:
Для всех, кто прочесывал Интернет, как я, — искал решение этой проблемы при тестировании с помощью Jest — я подкреплю ответ @biphobe, сказав, что Jest приведет к возникновению этой ошибки при экспорте чего-либо внутри того же файла, который вызывает ReactDOM.render.
В моем случае я экспортировал объект в свой index.js где я также вызывал ReactDOM.render. Я удалил этот экспорт и вуаля!
Комментарии:
1. Спасибо, это было именно то, что я искал. Явное упоминание о вызове
ReactDOM.render()
было именно тем, что мне нужно было, чтобы понять точную проблему!2. Рад, что это было полезно!
3. у меня возникла аналогичная проблема при попытке импортировать действия в моем тесте. К сожалению, удаление ReactDOM не помогает
4. @PraveenSrinivasan У меня возникли проблемы с Jest, когда я попытался вызвать ReactDOM.render и экспортировать что-то в одном файле javascript.
5. Ах да! Спасибо. Я добавил экспорт объекта в свой входной файл, который использует
ReactDom.render
. Но для этого файла пока нет тестов.
Ответ №2:
App.jsx
предполагается, что он экспортирует класс приложения и больше ничего не делает, render
должен вызываться в другом месте.
Если вы удалите render
вызов из App.ошибка jsx должна исчезнуть, она появляется, потому что тестовая среда не предоставляет DOM app
идентификатор.
Комментарии:
1. я получил ту же ошибку: — я протестирован с ava .. пожалуйста, скажите мне, где я должен ошибаться .. экспорт класса по умолчанию App расширяет компонент { render() { return ( <div> {this.props.children} </div> ) } } render((> <Путь маршрута = «/» компонент = {приложение}> <Компонент IndexRoute={Индекс} /> <Путь маршрута = «страница 1» компонент = {Страница 1} /> <Путь маршрута = «страница 2» компонент = {Страница 2} /> </Маршрут> </Маршрутизатор> ), document.getElementById(‘app’));
2. @BhavikKheni Пожалуйста, создайте новый вопрос и включите в него свой полный код вместе с ошибкой и трассировкой стека.
3. Где его следует вызывать, если нет, если не в App.jsx?
Ответ №3:
Как я вижу, эта ошибка возникает во многих случаях и требует разных подходов для ее решения. Мой сценарий отличается от приведенного выше примера, я использую redux amp; router, хотя я боролся с той же ошибкой. Что помогло мне решить эту проблему, так это перейти index.js
от:
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();
Комментарии:
1. очень полезный ответ — расстраивает, потому что я не могу понять, поддерживается ли это; Интересно, это потому, что мы используем Provider?
2. переход с «двойных кавычек» на «одинарные кавычки» сработал для меня, спасибо!!
3. @bibangamba я удивлен, что это имеет значение. Можете ли вы уточнить?
Ответ №4:
Я нашел решение для этой ошибки в моем случае использования: использование того же хранилища Redux, которое React использует за пределами React.
При попытке экспортировать Redux моего React store
из index.tsx
для использования где-то еще за пределами приложения React я получал ту же ошибку при выполнении тестов Jest (которые используют Enzyme) в App.tsx
файле.
Ошибка
Исходный код, который не работал при тестировании React, выглядел следующим образом.
// 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"),
);
Решение, которое сработало для меня
Разделите логику хранилища Redux в новый файл с именем store.ts
, затем создайте a default export
(для использования index.tsx
, т.Е. Приложением React) и экспорт по умолчанию с export const store
помощью (для использования из классов, отличных от React) следующим образом.
// 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"),
);
Использование хранилища Redux в классах, отличных от React
// MyClass.ts
import { store } from "./store"; // store.ts
export default class MyClass {
handleClick() {
store.dispatch({ ...new SomeAction() });
}
}
Экспорт default
Небольшое замечание, прежде чем вы уйдете. Вот как использовать default
экспорт и экспорт по умолчанию.
default export store;
используется сimport store from "./store";
export const store = ...
используется сimport { store } from "./store";
Надеюсь, это поможет!
https://nono.ma/says/solved-invariant-violation-target-container-is-not-a-dom-element
Комментарии:
1. Не приведет ли это к созданию двух хранилищ?
2. Разве мы не можем использовать
import store from "./store";
в наших файлах без реакции? Таким образом, избегая необходимости экспорта по умолчанию в store.ts3. Спасибо за ваш вопрос @IvOv. Я написал это год назад, и, похоже, в этом весь смысл. Мне пришлось бы протестировать его снова, но, насколько я помню, доступ к тому же хранилищу, к которому обращался React, не работал для меня, и именно поэтому мне пришлось использовать этот обходной путь.
4. Это решило проблему для меня!! Большое вам спасибо за это решение 🙂 вопрос об этом решении, если мы импортируем «хранилища» как в «MyClass.ts», так и в «index.ts», разве это не означает, что мы дважды вызывали createStore()? Спасибо! @Nono
5. Это было для меня. Потребовался небольшой рефакторинг, чтобы удалить
store
создание из index.tsx, но как только я переместил его в его собственный файл и импортировал оттуда, тест прошел. Почему jest не похож на хранилище, экспортируемое из index.tsx / js?
Ответ №5:
Убедитесь, что в вашем тестовом файле вы правильно импортировали компонент рендеринга. Он должен быть импортирован @testing-library/react
не из react-dom
:
import { render } from '@testing-library/react';
Ответ №6:
Ну, мы не можем запретить разработчикам экспортировать компонент из любого файла и тестировать его изолированно, даже если в нем есть импорт или использование react-dom.Я имею в виду, что в этом не так. Мы не пытаемся нарушить весь файл и протестировать некоторые его фрагменты, если это допустимый фрагмент кода .
У Jest нет проблем с react-dom , однако концептуально они разные . Предположительно, Jest — это виртуальная тестовая среда без браузера . React-DOM — это библиотека, которая выполняет сшивание виртуального DOM с реальным DOM для компонентов react . Настолько очевидно, что мы можем / не должны тестировать его обычным способом. Но это пока не обсуждается. мы в порядке, пока наши экспортируемые компоненты можно тестировать .
Итак, давайте издеваться над ним
Я сделал макет в файле TestSetup, настроенном с помощью «setupFilesAfterEnv» в конфигурации jest.
jest.mock("react-dom", () => {
return ({
"render": jest.fn()
})
})
Это в значительной степени сработало для меня. Мой код react и react-dom теперь удачно сочетаются в одном файле, работает как в браузере, так и в среде тестирования .
Я не сталкивался с какими-либо проблемами из-за этого. Если есть какие-либо, я буду смотреть в раздел комментариев
Ответ №7:
Это решение сработало для меня. Просто визуализируйте, если элемент есть:
const root = document.getElementById('root');
if (root) {
render(
<App />,
root,
);
}
Ответ №8:
Я обнаружил, что эта ошибка также может возникать при работе с Portals
вашими тестами. Если вы хотите пропустить ошибку, вы можете mock
Portals
либо добавить элемент Portal
контейнера в свой метод рендеринга:
render (
<div>
<TestedComponent />
<div id="portal" />
</div>
)