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

wnrlj8wa  于 2023-09-28  发布在  Jest
关注(0)|答案(4)|浏览(183)

按照本文blog post中介绍的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()的东西?
我有一个codesandbox,上面的代码可以在UserContext.jsUserContext.test.js中找到。
注意:测试可以在Tests选项卡下的codesandbox中运行。

kg7wmglp

kg7wmglp1#

正如你已经提到的,有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回调中恢复它们,以便在测试失败时也恢复它们。

hwamh0ep

hwamh0ep2#

你可以做这样的事

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);
});
iqih9akk

iqih9akk3#

它可能不是一个干净的解决方案,但它很简单,很容易弄清楚。这个例子使用了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");
});
gmxoilav

gmxoilav4#

对于全面覆盖和干净的控制台:

import { render, waitFor } from "@testing-library/react"

  test("should throw error when not wrapped inside `UserProvider`", async() => {
    jest.spyOn(console, 'error').mockImplementation(() => jest.fn());
    await waitFor(() => expect(() => render(<TestComponent />)
      .toThrow("Cannot use `useUser` outside of `UserProvider`")
    );
    jest.restoreAllMocks()
  });

相关问题