如何测试由 useEffect 上触发的 SetTimeout 修改的组件样式?

Анн*_*нна 5 reactjs jestjs enzyme ts-jest react-hooks

我正在使用 Jest/Enzyme 来测试 React/TypeScript 应用程序,并且我很难尝试编写一个测试来断言按钮是否在一段时间后显示:

这是要测试的组件的非常简化的版本:

import { StyledNotifyButton } from './styles'; //style-component element

const SomeComponent = (): ReactElement => {
  const [showNotifyButton, toggleNotifyButton] = useState(false);

  useEffect(() => {
    setTimeout(() => toggleNotifyButton(true), 5000);
  }, [toggleNotifyButton]);

  return (
    <div>
      <StyledNotifyButton visible={showNotifyButton} />
    </div>
  );
Run Code Online (Sandbox Code Playgroud)

这是测试:

 describe('< FailingTest >', () => {
  let wrapper: ReactWrapper;

  beforeAll(() => {
    wrapper = mount(<SomeComponent />);
  });

  it('should display the notify button only after X seconds', done => {
    let notifyButton = wrapper.find('StyledNotifyButton');

    jest.spyOn(React, 'useEffect').mockImplementation(f => f());
    expect(notifyButton.prop('visible')).toBe(false);

    jest.useFakeTimers();
    setTimeout(() => {
      wrapper.update();
      notifyButton = wrapper.find('NotifyButton');
      expect(notifyButton.prop('visible')).toBe(true);
      wrapper.unmount();
      done();
    }, 5000);
    jest.runAllTimers();
  });
Run Code Online (Sandbox Code Playgroud)

我已经尝试使用 fakeTimers、advanceTimersByTime、runAllTimers,如Just Timer Mocks中所述

我尝试过 setTimeouts,如此处讨论

手动触发 useEffect,如此此处

还有很多其他方式......但我总是得到

expect(received).toBe(expected) // Object.is equality

Expected: true
Received: false
Run Code Online (Sandbox Code Playgroud)

关于如何在超时后正确获取可见性变化的任何想法?谢谢!

sli*_*wp2 12

当组件被挂载useEffect并将被执行时,您需要在挂载组件之前setTimeout使用jest.useFakeTimers(implementation?: \'modern\' | \'legacy\') 。

\n

使用jest.runOnlyPendingTimers()

\n
\n

仅执行当前待处理的宏任务(即仅执行到目前为止已通过 setTimeout() 或 setInterval() 排队的任务)

\n
\n

或者jest.advanceTimersByTime(msT​​oRun)

\n

此外,我们需要将渲染它的代码包装起来并在act()调用中执行更新,因此jest.runOnlyPendingTimers()act().

\n

最后,我们需要调用wrapper.update()以确保状态反映到视图上。

\n

例如

\n

SomeComponent.tsx

\n
import React, { ReactElement, useEffect, useState } from \'react\';\nimport { StyledNotifyButton } from \'./styles\';\n\nexport const SomeComponent = (): ReactElement => {\n  const [showNotifyButton, toggleNotifyButton] = useState(false);\n\n  useEffect(() => {\n    setTimeout(() => {\n      toggleNotifyButton(true);\n    }, 5000);\n  }, [toggleNotifyButton]);\n\n  console.log(\'showNotifyButton: \', showNotifyButton);\n\n  return (\n    <div>\n      <StyledNotifyButton visible={showNotifyButton} />\n    </div>\n  );\n};\n
Run Code Online (Sandbox Code Playgroud)\n

styles.tsx

\n
import React from \'react\';\n\nexport function StyledNotifyButton({ visible }) {\n  return <button>click me</button>;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

SomeComponent.test.tsx

\n
import { mount, ReactWrapper } from \'enzyme\';\nimport React from \'react\';\nimport { act } from \'react-dom/test-utils\';\nimport { SomeComponent } from \'./SomeComponent\';\n\ndescribe(\'67440874\', () => {\n  let wrapper: ReactWrapper;\n\n  beforeAll(() => {\n    jest.useFakeTimers();\n    wrapper = mount(<SomeComponent />);\n  });\n  it(\'should pass\', () => {\n    let notifyButton = wrapper.find(\'StyledNotifyButton\');\n    expect(notifyButton.prop(\'visible\')).toBe(false);\n    act(() => {\n      jest.runOnlyPendingTimers();\n    });\n    wrapper.update();\n    expect(wrapper.find(\'StyledNotifyButton\').prop(\'visible\')).toBeTruthy();\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n

测试结果:

\n
 PASS  examples/67440874/SomeComponent.test.tsx\n  67440874\n    \xe2\x9c\x93 should pass (8 ms)\n\n  console.log\n    showNotifyButton:  false\n\n      at SomeComponent (examples/67440874/SomeComponent.tsx:13:11)\n\n  console.log\n    showNotifyButton:  true\n\n      at SomeComponent (examples/67440874/SomeComponent.tsx:13:11)\n\nTest Suites: 1 passed, 1 total\nTests:       1 passed, 1 total\nSnapshots:   0 total\nTime:        1.721 s\n
Run Code Online (Sandbox Code Playgroud)\n

软件包版本:

\n
import React, { ReactElement, useEffect, useState } from \'react\';\nimport { StyledNotifyButton } from \'./styles\';\n\nexport const SomeComponent = (): ReactElement => {\n  const [showNotifyButton, toggleNotifyButton] = useState(false);\n\n  useEffect(() => {\n    setTimeout(() => {\n      toggleNotifyButton(true);\n    }, 5000);\n  }, [toggleNotifyButton]);\n\n  console.log(\'showNotifyButton: \', showNotifyButton);\n\n  return (\n    <div>\n      <StyledNotifyButton visible={showNotifyButton} />\n    </div>\n  );\n};\n
Run Code Online (Sandbox Code Playgroud)\n