Jest · Unit tests that survive refactors

How do you test custom React hooks?

MediumhooksrenderHookreactUpdated Oct 2026
Quiz this track

Short answer

Use renderHook from Testing Library to mount a hook, then act on result, rerender and unmount. Prefer testing the hook through real UI when its behaviour is only meaningful in a component.

A custom hook is just a function that uses other hooks, so it needs a React renderer to run. renderHook creates a small test component internally and exposes the hook's return value through result.current. That makes it possible to test state transitions and cleanup without inventing a full component.

tsx
import { act, renderHook } from "@testing-library/react";
import { useCounter } from "./useCounter";

test("increments and resets", () => {
  const { result } = renderHook(() => useCounter(3));

  act(() => result.current.increment());
  expect(result.current.count).toBe(4);

  act(() => result.current.reset());
  expect(result.current.count).toBe(3);
});

What renderHook gives you

  • result.current for the latest returned value
  • rerender to change props and exercise updates
  • unmount to verify cleanup and cancellation
  • act or waitFor to wrap state updates and async effects

Prefer a component test when the hook is inseparable from rendering, such as a hook that returns props for a form. renderHook is best for reusable logic with a clear input and output: pagination, debouncing, media queries, form state machines. If the hook needs a provider, pass a wrapper to renderHook.

Testing every hook in isolation can produce a suite that passes while the composed UI is broken. Test the hook where its logic is complex and the integration where the wiring matters, not both everywhere.

Follow-ups you should expect

Interviewers use these to check whether the first answer was understanding or memorisation.

  1. 1How do you test a hook that needs a context provider?
  2. 2How do you verify that an effect cleans up?
  3. 3When is a component test better than renderHook?
  4. 4Why must state updates in a hook test be wrapped in act?

Say it in the interview

renderHook mounts a tiny component so the hook runs in a real React renderer. I read result.current, call the exposed actions inside act, and can rerender with new props or unmount to check cleanup. For hooks that are tightly coupled to UI, I test through a component instead. I use it mainly for reusable logic with a clear input and output.

Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.

Related questions

Reading is step one

Now say it out loud.

Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live Jest mock interview and get scored feedback on depth, clarity and confidence.