英文:
Error testing a React/D3 component with Jest and react testing library
问题
使用Jest和React测试库测试我的React组件。该组件使用类似以下的d3代码:
```javascript
useEffect(() => {
svg.transition()
.duration(500)
.call(zoom.transform, newTransform);
});
在测试脚本中,我渲染了我的组件,然后尝试等待500毫秒,直到过渡完成:
const sleep = async (timeout: number) => await new Promise((r) => setTimeout(r, timeout));
...
const { container } = render(<MyComponent width={...} height={...} data={...} />);
await sleep(500);
添加 sleep
导致测试中出现错误:
console.error
Error: Uncaught [TypeError: Cannot read properties of undefined (reading 'baseVal')]
at reportException (C:\...\node_modules\jsdom\lib\jsdom\living\helpers\runtime-script-errors.js:66:24)
at runAnimationFrameCallbacks (C:\...\node_modules\jsdom\lib\jsdom\browser\Window.js:605:13)
at Timeout.<anonymous> (C:\...\node_modules\jsdom\lib\jsdom\browser\Window.js:581:11)
at listOnTimeout (node:internal/timers:569:17)
at processTimers (node:internal/timers:512:7) {
没有 sleep
,组件的代码不会按预期执行过渡。
我的项目配置:
"dependencies": {
"d3": "^7.8.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.13.0",
"react-scripts": "^5.0.1",
"typescript": "^4.2.3"
},
"devDependencies": {
"@jest/globals": "29.1.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/d3": "^7.4.0",
"@types/jest": "^29.5.3",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"@types/react-router-dom": "^5.3.3",
"jest": "29.1.1",
"jest-environment-jsdom": "29.1.1",
"ts-jest": "29.1.1"
}
有什么建议如何解决这个问题?
<details>
<summary>英文:</summary>
I'm testing my React component with Jest and React testing library.The component uses d3 code like this:
useEffect(() => {
svg.transition()
.duration(500)
.call(zoom.transform, newTransform);
});
In the test script, I render my component, and then trying to wait 500ms until transition is done:
const sleep = async (timeout: number) => await new Promise((r) => setTimeout(r, timeout));
...
const {container} = render(<MyComponent width={...} height={...} data={...} />);
await sleep(500);
Adding `sleep` causes an error in the test:
console.error
Error: Uncaught [TypeError: Cannot read properties of undefined (reading 'baseVal')]
at reportException (C:...\node_modules\jsdom\lib\jsdom\living\helpers\runtime-script-errors.js:66:24)
at runAnimationFrameCallbacks (C:...\node_modules\jsdom\lib\jsdom\browser\Window.js:605:13)
at Timeout.<anonymous> (C:...\node_modules\jsdom\lib\jsdom\browser\Window.js:581:11)
at listOnTimeout (node:internal/timers:569:17)
at processTimers (node:internal/timers:512:7) {
Without `sleep`, the component's code does not execute transition as expected.
My project configuration:
"dependencies": {
"d3": "^7.8.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.13.0",
"react-scripts": "^5.0.1",
"typescript": "^4.2.3"
},
"devDependencies": {
"@jest/globals": "29.1.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/d3": "^7.4.0",
"@types/jest": "^29.5.3",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"@types/react-router-dom": "^5.3.3",
"jest": "29.1.1",
"jest-environment-jsdom": "29.1.1",
"ts-jest": "29.1.1"
}
Any suggestions how to solve the problem?
</details>
# 答案1
**得分**: 1
你可以尝试使用Jest的fakeTimers来控制测试中的定时器。这将允许你手动推进时间,而无需使用setTimeout。
使用jest.useFakeTimers()和act()与jest.advanceTimersByTime()一起,你可以控制测试环境中的定时器。
```jsx
jest.useFakeTimers(); // 使用Jest的假定时器
test('测试MyComponent过渡', () => {
const { container } = render(<MyComponent width={...} height={...} data={...} />);
// 可选地,你可以手动推进定时器以等待过渡完成
act(() => {
jest.advanceTimersByTime(500);
});
// 继续进行断言
...
jest.useRealTimers(); // 恢复真实定时器(可选,根据其他测试的需要)
});
此外,如果还没有这样做,也许可以将useEffect钩子包装在依赖数组中,以防止不必要的重新渲染。
英文:
May be you can try Jest's fakeTimers to control the timers in your tests. This will allow you to manually advance time without having to use setTimeout.
Using jest.useFakeTimers() and act() with jest.advanceTimersByTime(), you can control the timers in your test environment
jest.useFakeTimers(); // Use Jest's fake timers
test('Test MyComponent transition', () => {
const { container } = render(<MyComponent width={...} height={...} data={...} />)
// Optionally, you can wait for the transition to finish by manually advancing the timers
act(() => {
jest.advanceTimersByTime(500)
});
// Continue with your assertions
...
jest.useRealTimers() // Restore real timers (optional, depending on other tests)
})
Also, may be the useEffect hook could be wrapped with a dependency array if it isn't already, to prevent unnecessary re-renders?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论