首页 > 解决方案 > 使用 try/catch 测试 useEffect 钩子

问题描述

我需要catch在获取数据请求拒绝时进行测试,但我不明白为什么没有捕获到错误并且我收到了这个错误:UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)

我有这样的情况:

export const Container = ({fetchFirstAsset, fetchSecondAsset}) => {
  const [status, setStatus] = useState(null);

  async function fetchAssets() {
    setStatus(IN_PROGRESS);

    try {
      await fetchFirstAsset();
      await fetchSecondAsset()

      setStatus(SUCCESS);
    } catch {
      setStatus(FAILURE);
    }
  }

  useEffect(() => {
    fetchAssets();
  }, []);

  ....
};

并像这样测试:

import {mount} from 'enzyme';
import {act} from 'react-dom/test-utils';

const fetchFirstAsset = jest.fn();
const fetchSecondAsset = jest.fn();

it('should render without errors', async () => {
  fetchFirstAsset.mockReturnValueOnce(Promise.resolve());
  fetchSecondAsset.mockReturnValueOnce(Promise.reject());
  let component;

  await act(async () => {
    component = mount(
      <Container
        fetchFirstAsset={fetchFirstAsset}
        fetchSecondAsset={fetchSecondAsset}
      />
    );
  });

  expect(fetchSomething).toHaveBeenCalled();
});

如果我在fetchSomething使用 evertyhing 解决时Promise.resolve()测试用例工作正常并且测试是正确的,但是当我尝试Promise.reject()测试用例时,catch则不会捕获此错误并且我有unhandled promise rejection. (为什么代码看起来像这样:在应用程序的其他地方,我使用 redux 处理状态更改,因此对 catch 的测试很容易,但在一个地方我需要为组件获取 3 个不同的资产,我决定使用useState因为从 redux 中提取 3 种不同的状态并将其组合起来会很丑陋,useState我认为更干净)

提前感谢您的帮助!:)

标签: reactjsjestjsreact-hooksenzymeuse-effect

解决方案


我遇到了同样的问题,即在 useEffect() 中使用 try/catch 子句时不起作用。我做了一些搜索,看来这是一个潜在的错误,请查看:

https://github.com/testing-library/react-hooks-testing-library/issues/305

也就是说,我能够解决以下问题:

失败示例:

useEffect(() => {
  try {
      invokeMyAsyncFunction();  
      setStatus(SUCCESS);
  } catch (e) {
      setStatus(FAILURE);   // <== will not be invoked on exception!!
  }
}

成功案例:

useEffect(() => {
  invokeMyAsyncFunction()
     .then((response:any) => {
        setStatus(SUCCESS);
     })
     .catch((e) => {
        setStatus(FAILURE);   // <== this **WILL** be invoked on exception
     });
}

推荐阅读