Skip to main content

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:

GitHub


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: renderHook is 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.