react-hooks-testing-library
react-hooks-testing-library copied to clipboard
Cannot test hook raises an error
Dependencies:
- react 17.0.2
- @testing-library/[email protected]
- @testing-library/[email protected]
I have the following hook
function usePlan(): { loading: boolean, plan?: Plan, error?: Error } {
const [loading, setLoading] = useState(true);
const [plan, setPlan] = useState<Plan>();
const [error, setError] = useState<Error>();
useEffect(() => {
const tryFetchSettlementFees = async () => {
setLoading(true);
try {
const currentPlan = await client.getStorePlan();
setPlan(currentPlan);
setLoading(false);
setError(undefined);
} catch (error: any) {
setError(error);
setLoading(false);
if(!axios.isAxiosError(error)) throw error;
}
};
tryFetchSettlementFees();
}, [setLoading, setPlan]);
return {
loading,
error,
plan,
};
}
And I am trying to test the error is raised
it.only('throws when effect fails due to something not request related', () => {
const expectedError = new Error('expected error');
client.getStorePlan = jest.fn().mockRejectedValueOnce(expectedError);
let caughtError = null;
class ErrorBoundary extends React.Component {
constructor(props) {
super(props)
this.state = { hasError: false }
}
componentDidCatch(error) {
this.setState({ hasError: true })
caughtError = error
}
render() {
return !this.state.hasError && this.props.children
}
}
const wrapper = ({ children }) => <ErrorBoundary>{children}</ErrorBoundary>
const vals = renderHook(() => usePlan(), { wrapper });
expect(caughtError).toEqual(expectedError);
});
Also tried
const expectedError = new Error('expected error');
client.getStorePlan = jest.fn().mockRejectedValueOnce(expectedError);
expect(() => renderHook(() => usePlan())).toThrow(expectedError);
Also tried
const expectedError = new Error('expected error');
client.getStorePlan = jest.fn().mockRejectedValueOnce(expectedError);
const { current } = renderHook(() => usePlan());
expect(current.error?.message).toEqual(...);
But in all of them I cannot seem to get the error I expect, which is the useEffect throwing the error. However, I get on jest an unhandledPromiseRejection so I might be doing something wrong:
node:internal/process/promises:265
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "Error: expected error".] {
code: 'ERR_UNHANDLED_REJECTION'
}
So what is happening here is that your promise (tryFetchSettlementFees()
will return a promise) is rejecting, but nothing is catching it so there is no way to test for it. Try changing the call in your effect to this to see what I mean:
tryFetchSettlementFees().catch((err) => console.error(err));
There isn't a way (that I know of) to capture the request promise into the effect itself without storing the result/error into state like you are doing, so you likely just want to move the throw outside of the effect, something like:
function usePlan(): { loading: boolean, plan?: Plan, error?: Error } {
const [loading, setLoading] = useState(true);
const [plan, setPlan] = useState<Plan>();
const [error, setError] = useState<Error>();
useEffect(() => {
const tryFetchSettlementFees = async () => {
setLoading(true);
try {
const currentPlan = await client.getStorePlan();
setPlan(currentPlan);
setLoading(false);
setError(undefined);
} catch (error: any) {
setError(error);
setLoading(false);
}
};
tryFetchSettlementFees();
}, [setLoading, setPlan]);
if(error && !axios.isAxiosError(error)) throw error;
return {
loading,
error,
plan,
};
}
(note this is entirely untested)