Reputation: 99
Using React, Jest and react testing library
My custom hook
import { useContext } from "react";
import { SelectThemeContext } from "../contexts/SelectThemeContext/SelectThemeContextProvider";
import { isEmpty } from "../utils/functions/isEmpty";
const useSelectThemeContext = () => {
const context = useContext(SelectThemeContext);
if (isEmpty(context)) {
throw Error(
"You have to use useSelectThemeContext inside <SelectThemeContextProvider />",
);
}
const { theme, toggleTheme } = context;
return { theme, toggleTheme };
};
export default useSelectThemeContext;
My test
it("[ACAMP-03] - should return error message when trying to use custom hook without provider", async () => {
let message: string;
try {
await renderHook(() => useSelectThemeContext());
message = "";
} catch (e: any) {
message = e.message;
}
expect(message).toEqual(
"You have to use useSelectThemeContext inside <SelectThemeContextProvider />",
);
});
I can get the message but I also get an error in the console.
at VirtualConsole. (node_modules/jsdom/lib/jsdom/virtual-console.js:29:45)
at reportException (node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:70:28)
at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:341:9)
at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:231:34)
console.error
The above error occurred in the <TestComponent> component:
at TestComponent (C:\Users\rafael.almeida\Documents\Projects\Estudo\acamp\base-reactv18-eslint-prettier-config\node_modules\@testing-library\react\dist\pure.js:281:5)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
at logCapturedError (node_modules/react-dom/cjs/react-dom.development.js:18687:23)
at update.callback (node_modules/react-dom/cjs/react-dom.development.js:18720:5)
at callCallback (node_modules/react-dom/cjs/react-dom.development.js:13923:12)
at commitUpdateQueue (node_modules/react-dom/cjs/react-dom.development.js:13944:9)
at commitLayoutEffectOnFiber (node_modules/react-dom/cjs/react-dom.development.js:23391:13)
at commitLayoutMountEffects_complete (node_modules/react-dom/cjs/react-dom.development.js:24688:9)
at commitLayoutEffects_begin (node_modules/react-dom/cjs/react-dom.development.js:24674:7)
at commitLayoutEffects (node_modules/react-dom/cjs/react-dom.development.js:24612:3)
Run test CodeSandBox Test CodeSandBox
Upvotes: 2
Views: 3001
Reputation: 407
From this issue that renderHook
for React 18 should be imported from @testing-library/react
which does not expose the result.error
property like the one exported from @testing-library/react-hooks
. And there is another issue that seems to be stalled since they are requiring a documented API before agreeing to build this functionality back in. So for now here is what I'm doing in order to catch the error and not console.error the expected error:
it('should error with message', () => {
const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
try {
renderHook(() => failingHook());
} catch (error) {
expect((error as Error).message).toEqual('Expected error');
}
spy.mockRestore();
});
Upvotes: 0
Reputation: 1
I was able to test and cover the error handling line by mocking the useContext return.
import React from 'react';
import { renderHook } from '@testing-library/react';
import { useSelectThemeContext} from '../your-path';
describe('useSelectThemeContext', () => {
it("[ACAMP-03] - should return error message when trying to use custom hook without provider", () => {
jest.spyOn(React, 'useContext').mockImplementation(() => null)
try {
renderHook(useSelectThemeContext);
}
catch (error) {
expect(error).toEqual(new Error("You have to use useSelectThemeContext inside <SelectThemeContextProvider />"))
}
});
})
Upvotes: 0
Reputation: 99
i'm using a means to suppress the console.error specifically for testing, if the algorithm is designed for a way to not need to use this please...
const spyConsole = jest
.spyOn(console, "error")
.mockImplementation(() => {});
Upvotes: 1
Reputation: 828
You can/should use @testing-library/react-hooks. It provides a different renderHook method that can accept a wrapper parameter, and can return errors.
Your test would then look something like this. I don't believe it has to be async. If your specific requirements require an async function, then refer to the jest docs. I remember this was a bit different than what I expected for testing async.
import { renderHook } from "@testing-library/react-hooks";
it("[ACAMP-03] - should return error message when trying to use custom hook without provider", () => {
const { result } = renderHook(() => useSelectedThemeContext());
expect(result.error?.message).toEqual(
"You have to use useSelectThemeContext inside <SelectThemeContextProvider />",
);
});
If you want to test the functionality with the provider you can do something like this.
it("does what the hook is supposed to do", () => {
const wrapper = ({ children }) => (
<SelectedThemeContextProvider>
{children}
</SelectedThemeContextProvider>
);
const { result } = renderHook(() => useSelectedThemeContext(), {wrapper});
expect(result.error).toBeUndefined();
// using color as an example
expect(result.color).toEqual("#ffffff");
})
Upvotes: 1