Following Kent C Dodds' provider pattern explained in this blog post, I have a context provider component along with a hook to use that context.
The hook guards against the use of it outside of the provider,
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 };
}
To test the scenario, I create a TestComponent
and use the useUser
hook inside it.
function TestComponent() {
const { user, switchUser } = useUser();
return (
<>
<p>User: {user.name}</p>
<button onClick={switchUser}>Switch user</button>
</>
);
}
I test it like this,
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;
});
I currently have to mock console.error
and later set it to its original value at the end of the test. It works. But I'd like to make this more declarative and simpler. Is there a good pattern to achieve it?
Something using .toThrow() perhaps?
I have a codesandbox for this, the above code can be found in UserContext.js
and UserContext.test.js
.
Note: Tests can be run in the codesandbox itself under the Tests
tab.
Error: Uncaught [Error: Cannot use
useUser` outside ofUserProvider
]` – Ambrosane