React 测试 - 组件访问 window 对象时出错
React test - error when component accesses window object
出于特定原因,我在 public
文件夹的 index.html
文件中使用了一个脚本,该脚本在 window
对象上创建了一个 属性。 属性 是一个异步函数。这是我在 public/index.html:
中创建的脚本
<script>
const txt = "Hello Word";
async function waitText() {
try {
const data = await fetch(
"https://jsonplaceholder.typicode.com/all/1"
);
const text = await data.json();
returntext;
} catch (error) {
console.error(error);
return "Error";
}
}
Object.defineProperty(window, "waitText", { value: waitText });
</script>
我的 App 组件首先渲染一个在 useState 中指定的 Loading... 文本,然后 useEffect 中的一个异步函数调用 windom 对象的函数,等待响应并执行 setState 并更改 Loading.. . 文本到它从 api 得到的那个。到目前为止,该应用程序可以正常运行。这里的代码是 App.tsx
import logo from "./logo.svg";
import "./App.css";
import { useEffect, useState } from "react";
function App() {
const [txt, setTxt] = useState("Loading...");
useEffect(() => {
const getTxt = async() => {
try {
const text = await window.waitText();
setTxt(text.title);
} catch (error) {
console.error(error);
}
};
getTxt();
}, []);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>{txt}</p>
</header>
</div>
);
}
export defaultApp;
到目前为止,该应用程序运行良好。问题是用 jest 和 react-testing-library 做测试。这里的文本代码:
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', async () => {
render(<App />);
screen.debug()
const linkElement = await screen.findByText("delectus aut autem");
expect(linkElement).toBeInTheDocument();
});
当运行这个测试时,调用window.waitText()时在useEffect区域出现错误。显然,在执行玩笑测试时,它不会执行 index.html 中的脚本,因此 属性 永远不会在 window 对象中创建,因此在 useEffect 和文字无效。
从 public/index.html 中删除脚本不是解决方案。 好吧,虽然这不是一个好的做法,但在某些情况下这是必要的。
create-react-app 文档是这样说的:
Some libraries may be incompatible with webpack and you have no choice but to include it as a tag.
请问如何让这个组件通过测试?
您可以在测试用例中呈现组件之前模拟 window.waitText()
方法及其 resolve/rejected 值。
例如
App.jsx
:
import { useEffect, useState } from 'react';
import React from 'react';
function App() {
const [txt, setTxt] = useState('Loading...');
useEffect(() => {
const getTxt = async () => {
try {
const text = await window.waitText();
setTxt(text.title);
} catch (error) {
console.error(error);
}
};
getTxt();
}, []);
return (
<div className="App">
<header className="App-header">
<p>{txt}</p>
</header>
</div>
);
}
export default App;
App.test.jsx
:
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import App from './App';
test('renders learn react link', async () => {
Object.defineProperty(window, 'waitText', { value: () => Promise.resolve({ title: 'delectus aut autem' }) });
render(<App />);
const linkElement = await screen.findByText('delectus aut autem');
expect(linkElement).toBeInTheDocument();
});
测试结果:
PASS Whosebug/72355656/App.test.jsx (13.14 s)
✓ renders learn react link (33 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 92.31 | 100 | 100 | 91.67 |
App.jsx | 92.31 | 100 | 100 | 91.67 | 13
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 14.9 s, estimated 15 s
出于特定原因,我在 public
文件夹的 index.html
文件中使用了一个脚本,该脚本在 window
对象上创建了一个 属性。 属性 是一个异步函数。这是我在 public/index.html:
<script>
const txt = "Hello Word";
async function waitText() {
try {
const data = await fetch(
"https://jsonplaceholder.typicode.com/all/1"
);
const text = await data.json();
returntext;
} catch (error) {
console.error(error);
return "Error";
}
}
Object.defineProperty(window, "waitText", { value: waitText });
</script>
我的 App 组件首先渲染一个在 useState 中指定的 Loading... 文本,然后 useEffect 中的一个异步函数调用 windom 对象的函数,等待响应并执行 setState 并更改 Loading.. . 文本到它从 api 得到的那个。到目前为止,该应用程序可以正常运行。这里的代码是 App.tsx
import logo from "./logo.svg";
import "./App.css";
import { useEffect, useState } from "react";
function App() {
const [txt, setTxt] = useState("Loading...");
useEffect(() => {
const getTxt = async() => {
try {
const text = await window.waitText();
setTxt(text.title);
} catch (error) {
console.error(error);
}
};
getTxt();
}, []);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>{txt}</p>
</header>
</div>
);
}
export defaultApp;
到目前为止,该应用程序运行良好。问题是用 jest 和 react-testing-library 做测试。这里的文本代码:
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', async () => {
render(<App />);
screen.debug()
const linkElement = await screen.findByText("delectus aut autem");
expect(linkElement).toBeInTheDocument();
});
当运行这个测试时,调用window.waitText()时在useEffect区域出现错误。显然,在执行玩笑测试时,它不会执行 index.html 中的脚本,因此 属性 永远不会在 window 对象中创建,因此在 useEffect 和文字无效。
从 public/index.html 中删除脚本不是解决方案。 好吧,虽然这不是一个好的做法,但在某些情况下这是必要的。
create-react-app 文档是这样说的:
Some libraries may be incompatible with webpack and you have no choice but to include it as a tag.
请问如何让这个组件通过测试?
您可以在测试用例中呈现组件之前模拟 window.waitText()
方法及其 resolve/rejected 值。
例如
App.jsx
:
import { useEffect, useState } from 'react';
import React from 'react';
function App() {
const [txt, setTxt] = useState('Loading...');
useEffect(() => {
const getTxt = async () => {
try {
const text = await window.waitText();
setTxt(text.title);
} catch (error) {
console.error(error);
}
};
getTxt();
}, []);
return (
<div className="App">
<header className="App-header">
<p>{txt}</p>
</header>
</div>
);
}
export default App;
App.test.jsx
:
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import App from './App';
test('renders learn react link', async () => {
Object.defineProperty(window, 'waitText', { value: () => Promise.resolve({ title: 'delectus aut autem' }) });
render(<App />);
const linkElement = await screen.findByText('delectus aut autem');
expect(linkElement).toBeInTheDocument();
});
测试结果:
PASS Whosebug/72355656/App.test.jsx (13.14 s)
✓ renders learn react link (33 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 92.31 | 100 | 100 | 91.67 |
App.jsx | 92.31 | 100 | 100 | 91.67 | 13
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 14.9 s, estimated 15 s