繁体   English   中英

使用 testing-library 和 jest 的 React 组件抛出的测试错误

[英]Testing error thrown by a React component using testing-library and jest

遵循 Kent C Dodds 在这篇文中解释的提供程序模式,我有一个上下文提供程序组件以及一个使用该上下文的钩子。

钩子防止在提供者之外使用它,

export function useUser() {
  const { user } = useContext(UserContext) || {};
  const { switchUser } = useContext(SwitchUserContext) || {};
  if (!user || !switchUser) {
    throw new Error('Cannot use `useUser` outside of `UserProvider`');
  }
  return { user, switchUser };
}

为了测试场景,我创建了一个TestComponent并在其中使用了useUser挂钩。

function TestComponent() {
  const { user, switchUser } = useUser();
  return (
    <>
      <p>User: {user.name}</p>
      <button onClick={switchUser}>Switch user</button>
    </>
  );
}

我是这样测试的

  test('should throw error when not wrapped inside `UserProvider`', () => {
    const err = console.error;
    console.error = jest.fn();
    let actualErrorMsg;
    try {
      render(<TestComponent />);
    } catch(e) {
      actualErrorMsg = e.message;
    }
    const expectedErrorMsg = 'Cannot use `useUser` outside of `UserProvider`';
    expect(actualErrorMsg).toEqual(expectedErrorMsg);

    console.error = err;
  });

我目前必须模拟console.error ,然后在测试结束时将其设置为原始值。 有用。 但我想让它更具声明性和更简单。 有没有好的模式来实现它? 也许使用.toThrow() 的东西?

我有一个代码,上面的代码可以在UserContext.jsUserContext.test.js中找到。

注意:测试可以在Tests选项卡下的代码框本身中运行。

正如你已经提到的,有expect().toThrow() :)

所以在你的情况下:

  test("should throw error when not wrapped inside `UserProvider`", () => {
    expect(() => render(<TestComponent />))
      .toThrow("Cannot use `useUser` outside of `UserProvider`");
  });

关于console.error :目前没有办法关闭默认错误日志。 如果你想隐藏错误,你仍然需要模拟console.error

当您模拟console.error之类的函数时,您希望在afterEach回调中恢复它们,以便在测试失败时也恢复它们。

你可以做这样的事情

test('should throw error when not wrapped inside `UserProvider`', () => {
  component.useUser = jest.fn().mockRejectedValue(new Error('Cannot use `useUser` outside of `UserProvider`'));
  let actualErrorMsg;
  try {
    render(<TestComponent />);
  } catch(e) {
    actualErrorMsg = e.message;
  }
  const expectedErrorMsg = 'Cannot use `useUser` outside of `UserProvider`';
  expect(actualErrorMsg).toEqual(expectedErrorMsg);
});

它可能不是一个干净的解决方案,但它很简单,很容易弄清楚。 此示例使用 TypeScript 但不使用也可以正常工作。 设置一次这样的东西并在其他地方重用它也相当容易。

it("errs if provider is missing", () => {
  const HookWrapper = ({
    testId,
  }: {
    testId?: string;
  }) => {
    try {
      const data = useCustomHook();

      return <pre data-testid={testId}>{JSON.stringify({ data }, null, 2)}</pre>;
    } catch (err) {
      const error = err as Error;
      const errorPayload = { message: error.message, stack: error.stack };
      return (
        <pre data-testid={testId}>
          {JSON.stringify({ error: errorPayload }, null, 2)}
        </pre>
      );
    }
  };
  
  render(<HookWrapper testId="HookWrapper" />);

  const providedData = JSON.parse(
    screen.getByTestId("HookWrapper").innerHTML
  );
  const error = providedData.error as Error;

  expect(error).toBeDefined();
  expect(error.message).toEqual("SomeProvider Context not initialized");
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM