Dùng renderHook của React Testing Library — nó render một component rỗng chỉ để gọi hook, trả về result.current là giá trị hook trả ra.
it("increments the counter", () => {
const { result } = renderHook(() => useCounter(0))
expect(result.current.count).toBe(0)
act(() => result.current.increment()) // state updates must be wrapped
expect(result.current.count).toBe(1)
})Ba điểm hay sai:
- result.current là snapshot. Không destructure ra biến (const { count } = result.current) rồi assert sau khi update — biến đó vẫn giữ giá trị cũ. Luôn đọc lại qua result.current.
- Mọi thứ làm state đổi phải bọc act, nếu không React cảnh báo và DOM chưa kịp cập nhật. Với update async thì await waitFor(() => expect(result.current.data).toBeDefined()).
- Hook cần provider thì truyền wrapper: renderHook(() => useCart(), { wrapper: CartProvider }). Đổi input thì dùng rerender(newProps) từ giá trị trả về.
Nếu hook chỉ được dùng bởi một component và không phải API công khai, test qua chính component đó thường có giá trị hơn: nó kiểm tra luôn phần nối dây. renderHook hợp lý cho hook dùng lại nhiều nơi hoặc logic phức tạp (debounce, pagination, form state).