[英]Unable to catch error thrown inside a React component when testing with testing-library
[英]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.js
和UserContext.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.