[英]Trouble testing React component render with Jest and Testing-Library because there's no Document available to Jest
I'm trying to use Jest and React-Testing-Library to test the render of a React component <Main/>, but when I run the test, the client
that handles fetch
throws an error because it's using document.querySelector()
- but when Jest runs there's no document
because there's no browser rendered.我正在尝试使用 Jest 和 React-Testing-Library 来测试 React 组件 <Main/> 的呈现,但是当我运行测试时,处理
fetch
的client
会抛出错误,因为它正在使用document.querySelector()
-但是当 Jest 运行时没有document
,因为没有呈现浏览器。
My Goal Here: Get Jest and RTL set up so we can start writing tests for all the components.我的目标是:设置 Jest 和 RTL,以便我们可以开始为所有组件编写测试。 I would like to start with verifying that I can render
<Main/>
without errors.我想首先验证我可以无错误地渲染
<Main/>
。
Here's Client.js
这是
Client.js
class Client {
constructor() {
console.log("Client initializing")
console.log(document.querySelector('meta[name="csrf-token"]'))
console.log(document)
this.token = document.querySelector('meta[name="csrf-token"]').content;
}
getData(path) {
return (
fetch(`${window.location.origin}${path}`, {
headers: { "X-CSRF-Token": this.token }
})
)
}
submitData(path, method, body) {
return (
fetch(`${window.location.origin}${path}`, {
method: method,
headers: { "X-CSRF-Token": this.token },
body: body
})
)
}
deleteData(path) {
return (
fetch(`${window.location.origin}${path}`, {
method: "DELETE",
headers: {
"X-CSRF-Token": this.token,
"Content-Type": "application/json"
}
})
)
}
}
export default Client;
Here's main.test.js
:这是
main.test.js
:
/**
* @jest-environment jsdom
*/
import React from 'react';
import { render, screen } from '@testing-library/react';
// import userEvent from '@testing-library/user-event';
import Main from '../../app/javascript/components/Main';
test("renders without errors", ()=> {
render(<Main/>);
});
I've also set up a setupTests.js
file:我还设置了一个
setupTests.js
文件:
require("jest-fetch-mock").enableMocks();
import '@testing-library/jest-dom';
And called it here in package.json
:并在
package.json
中调用它:
"jest": {
"roots": [
"test/javascript"
],
"moduleNameMapper": {
"\\.(svg|png)": "<rootDir>/__mocks__/svgrMock.js"
},
"automock": false,
"setupFilesAfterEnv": [
"./test/javascript/setupTests.js"
]
},
I have also set testEnvironment: 'jsdom'
in the jest.config.js
file.我还在
jest.config.js
文件中设置了 testEnvironment testEnvironment: 'jsdom'
。
When I run yarn jest
I get the following error: TypeError: Cannot read properties of null (reading 'content')
which points to this.token = document.querySelector('meta[name="csrf-token"]').content;
当我运行
yarn jest
时,出现以下错误: TypeError: Cannot read properties of null (reading 'content')
which points to this.token = document.querySelector('meta[name="csrf-token"]').content;
in Client.js
在
Client.js
This makes sense to me because it's looking for a DOM element, but Jest runs in Node (no browser render) so there's no DOM to be found.这对我来说很有意义,因为它正在寻找 DOM 元素,但是 Jest 在 Node 中运行(没有浏览器渲染)所以找不到 DOM。
I think I need to:我想我需要:
document
so the app can run without being rendered in the browser.document
,以便应用程序可以在不在浏览器中呈现的情况下运行。 Not sure how to do this.setupTests.js
) including many permutations of something like this: setupTests.js
),包括许多类似这样的排列:import { TextDecoder, TextEncoder } from 'util'
global.TextEncoder = TextEncoder
global.TextDecoder = TextDecoder
//variables to mock a csrf token
const csrfToken = 'abcd1234';
const virtualDom = `
<!doctype html>
<head>
<meta name="csrf-token" content="${csrfToken}" />
</head>
<body>
<form>
<input type="hidden" name="csrf-token" value=${csrfToken}>
</form>
</body>
</html>
`;
const { JSDOM } = require("jsdom");
//mock a page passing virtualDom to JSDOM
const page = new JSDOM(virtualDom);
const { window } = page;
function copyProps(src, target) {
const props = Object.getOwnPropertyNames(src)
.filter(prop => typeof target[prop] === 'undefined')
.map(prop => Object.getOwnPropertyDescriptor(src, prop));
Object.defineProperties(target, props);
}
global.window = window;
global.document = window.document;
global.navigator = {
userAgent: 'node.js',
};
copyProps(window, global);
But global.window = window
never seems to work because if I declare that and then immediately console.log(global.window, window)
i get null and then a window.但是
global.window = window
似乎从来没有工作,因为如果我声明然后立即console.log(global.window, window)
我得到 null 然后是 window。
I don't know how to do that properly either tbh, but I also know downgrading React is probably the wrong path here.我也不知道如何正确地做到这一点,但我也知道降级 React 可能是错误的路径。
yarn
yarn
Jest uses jsdom
under the hood, so it's not true that "there is no DOM", however all the page is instantiated in the test - nothing comes from server(and jsdom
neither supports navigation to request real page from the server and then continue testing). Jest 在后台使用
jsdom
,所以“没有 DOM”是不正确的,但是所有页面都在测试中实例化了 - 没有来自服务器的东西(并且jsdom
既不支持导航以从服务器请求真实页面然后继续测试).
Therefore we need to render element in the test:因此我们需要在测试中渲染元素:
render(<>
<meta name="csrf-token" content="mocked-token" />
<Main/>
</>);
Though I'm not sure why complete page replacement you tried in globalSetup did not work, maybe Jest does not allow to override JSDOM instance this way and binds earlier than globalSetup.js is ever run.虽然我不确定为什么您在 globalSetup 中尝试的完整页面替换不起作用,但也许 Jest 不允许以这种方式覆盖 JSDOM 实例并且在运行 globalSetup.js 之前绑定。
Turns out I had to mock all the data coming in to the components that render from Main.事实证明,我必须模拟所有进入从 Main 呈现的组件的数据。 (In addition to the CSRF token mocking)
(除了 CSRF 令牌模拟)
Once I did that, everything else fell into place.一旦我这样做了,其他一切就都到位了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.