[英]Testing React: Target Container is not a DOM element
我正在嘗試在使用 Webpack 時使用 Jest/Enzyme 測試 React 組件。
我有一個非常簡單的測試@
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 />
的導入,這會導致失敗。 堆棧跟蹤顯示App.js
的第 14 行是失敗的原因——這只不過是來自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?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'
}
]
}
}
還有我的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 的新更新有關嗎?
對於像我一直在通過互聯網進行梳理的其他任何人 - 在使用 Jest 進行測試時尋找解決方案 - 我將支持 @biphobe 的答案,說 Jest 會在您導出內部內容時發生此錯誤調用 ReactDOM.render 的同一個文件。
在我的例子中,我在我的 index.js 中導出了一個對象,我也在其中調用了 ReactDOM.render。 我刪除了這個出口,瞧!
App.jsx
應該導出 App 類並且什么都不做,應該在其他地方調用render
。
如果您從 App.jsx 中刪除render
調用,錯誤應該消失,它會彈出,因為測試環境沒有為 DOM 提供app
ID。
正如我所看到的,這個錯誤在很多情況下都會出現,需要不同的方法來解決它。 我的場景與上面的示例不同,我使用redux & 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();
我為我的用例找到了這個錯誤的解決方案:使用相同的 Redux 存儲 React 在 React 之外使用。
在嘗試從index.tsx
導出我的 React 的 Redux store
以在 React 應用程序之外的其他地方使用時,我在App.tsx
文件中運行 Jest 測試(使用 Enzyme)時遇到了同樣的錯誤。
測試 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
的新文件中,然后創建一個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"),
);
// 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
確保在您的測試文件中已經很好地導入了渲染組件。 它應該從@testing-library/react
導入,而不是從react-dom
導入:
import { render } from '@testing-library/react';
好吧,我們不能阻止開發人員從任何文件中導出組件並單獨測試它,即使它有react-dom導入或使用。我的意思是它有什么問題。 我們不會試圖干擾整個文件並測試其中的一些片段,只要那是一段有效的代碼。
Jest 對react-dom沒有問題,但從概念上講它們是 diff 。 Jest 應該是一個無瀏覽器的虛擬測試環境。 React-DOM 是一個將虛擬 DOM 拼接到真實 DOM 以用於反應組件的庫。 如此明顯,我們可以/不應該以正常方式對其進行測試。 但這不是現在的討論。 只要我們導出的組件是可測試的,我們就可以了。
所以讓我們嘲笑它
我在jest config中使用“setupFilesAfterEnv”配置的 testSetup 文件中進行了模擬。
jest.mock("react-dom", () => {
return ({
"render": jest.fn()
})
})
這對我來說非常有用。 我的 react 和 react-dom 代碼現在很高興地放在一個文件中,可以在瀏覽器和測試環境中運行。
因此,我沒有遇到任何問題。 有的話我會看評論區
這個解決方案對我有用。 如果元素存在,只需渲染:
const root = document.getElementById('root');
if (root) {
render(
<App />,
root,
);
}
我發現在測試中使用Portals
時也會引發此錯誤。 如果您想跳過錯誤,您可以mock
Portals
或在您的渲染方法中添加Portal
容器元素:
render (
<div>
<TestedComponent />
<div id="portal" />
</div>
)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.