错误测试一个使用Jest和React测试库测试的React/D3组件

huangapple go评论96阅读模式
英文:

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&#39;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&#39;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&#39;s fake timers

test(&#39;Test MyComponent transition&#39;, () =&gt; {
  const { container } = render(&lt;MyComponent width={...} height={...} data={...} /&gt;)

  // Optionally, you can wait for the transition to finish by manually advancing the timers
  act(() =&gt; {
    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?

huangapple
  • 本文由 发表于 2023年7月17日 20:32:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76704484.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定