Testing Tanstack Query
Overview
This document provides an in-depth explanation of test cases written for the useNotes hook using Vitest and @testing-library/react. It covers assertions for fetching, creating, and updating notes, along with an explanation of renderHook in React 18.
info
Know more about React Testing Library.
For more test cases and implementation details, check out the complete repository:
Setting up TanStack Query Testing in React 18
Before testing the useNotes hook, we need to properly set up the TanStack Query (React Query) provider to ensure a new QueryClient instance is created for each test.
Setting up wrapper
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { vi } from 'vitest';
import notesService from '@/services/notes.service';
vi.mock('@/services/notes.service');
describe('useNotes hook', () => {
let queryClient;
beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
});
const wrapper = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
// Tests follow...
});
This setup ensures that each test has an isolated instance of QueryClient, preventing state leakage between tests.
Explanation of Assertions
1. Fetching Notes Successfully
it('should fetch notes successfully', async () => {
const mockNotes = [
{ id: 1, title: 'Test Note 1', content: 'Content 1' },
{ id: 2, title: 'Test Note 2', content: 'Content 2' }
];
vi.mocked(notesService.getNotes).mockResolvedValue(mockNotes);
const { result } = renderHook(() => useNotes(), { wrapper });
await waitFor(() => {
return result.current.notes !== undefined;
});
expect(result.current.isLoading).toBe(false);
expect(result.current.isError).toBe(false);
expect(result.current.notes).toEqual(mockNotes);
});
2. Creating a Note Successfully
it('should create a note successfully', async () => {
const newNote = { note_id: 1, title: "Test Note", body: "<p>Note body</p>", author: 1, created: new Date().toISOString(), modified: new Date().toISOString() };
vi.mocked(notesService.createNote).mockResolvedValue(newNote);
vi.mocked(notesService.getNotes).mockResolvedValue([newNote]);
const { result } = renderHook(() => useNotes(), { wrapper });
await result.current.createMutation.mutateAsync(newNote);
await waitFor(() => {
expect(notesService.createNote).toHaveBeenCalledWith(newNote);
expect(result.current.createMutation.isSuccess).toBe(true);
});
});
3. Updating a Note Successfully
it('should update a note successfully', async () => {
const existingNote = { id: 1, title: 'Original Title', content: 'Original Content' };
const updatedData = { title: 'Updated Title' };
vi.mocked(notesService.updateNote).mockResolvedValue({
...existingNote,
...updatedData
});
vi.mocked(notesService.getNotes).mockResolvedValue([{
...existingNote,
...updatedData
}]);
const { result } = renderHook(() => useNotes(), { wrapper });
await result.current.updateMutation.mutateAsync({
noteId: existingNote.id,
updatedData
});
await waitFor(() => {
expect(notesService.updateNote).toHaveBeenCalledWith(existingNote.id, updatedData);
expect(result.current.updateMutation.isSuccess).toBe(true);
});
});
Why renderHook is Essential in React 18
- Purpose:
renderHookis used to test React hooks in isolation. It allows you to render a hook and interact with its return values and effects. - Significance:
- Tests hook logic without rendering a full component.
- Verifies state management, side effects, and mutations.
- Helps isolate behavior, making debugging easier.
📌 GitHub Repository
For more test cases and the complete implementation, visit the GitHub repository.