From harness-claude
Tests Next.js App Router components, Server Actions, and Route Handlers using Jest, Vitest, and MSW. Covers mocking next/navigation, next/headers, and direct handler invocation.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:next-testing-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Test App Router components, Server Actions, and Route Handlers with Jest, Vitest, and MSW
Test App Router components, Server Actions, and Route Handlers with Jest, Vitest, and MSW
@testing-library/react for rendering Client Components — import from @testing-library/react as usual.next/navigation (useRouter, usePathname, useSearchParams) with jest.mock() or vi.mock() — these hooks are not available in tests.next/headers (cookies, headers) for Server Components that read request headers or cookies.msw) to intercept fetch() calls in integration tests — set up a server with setupServer() and start/stop per test suite.NextRequest and calling the handler directly — no HTTP server needed.next-router-mock as a drop-in useRouter mock that supports assertions on navigation calls.// __tests__/server-component.test.tsx — testing a Server Component
import { render, screen } from '@testing-library/react';
import { PostList } from '@/components/post-list'; // Server Component
// Mock data access module
vi.mock('@/lib/posts', () => ({
getPosts: vi.fn().mockResolvedValue([
{ id: '1', title: 'Hello World', slug: 'hello-world' },
]),
}));
test('renders post titles', async () => {
const component = await PostList(); // call as async function
render(component);
expect(screen.getByText('Hello World')).toBeInTheDocument();
});
// __tests__/client-component.test.tsx — Client Component with router mock
import { render, screen, fireEvent } from '@testing-library/react';
import { useRouter } from 'next/navigation';
import { NavButton } from '@/components/nav-button';
vi.mock('next/navigation', () => ({ useRouter: vi.fn() }));
test('navigates on click', () => {
const push = vi.fn();
vi.mocked(useRouter).mockReturnValue({ push } as any);
render(<NavButton href="/dashboard">Go to Dashboard</NavButton>);
fireEvent.click(screen.getByText('Go to Dashboard'));
expect(push).toHaveBeenCalledWith('/dashboard');
});
// __tests__/route-handler.test.ts — Route Handler unit test
import { GET } from '@/app/api/posts/route';
import { NextRequest } from 'next/server';
test('returns posts as JSON', async () => {
const request = new NextRequest('http://localhost/api/posts');
const response = await GET(request);
expect(response.status).toBe(200);
const data = await response.json();
expect(Array.isArray(data)).toBe(true);
});
Testing Next.js App Router applications requires different strategies for Server Components, Client Components, and server-side functions — they run in different environments.
Server Component testing: Server Components are async functions — call them directly and render the result. The key is mocking their dependencies (database calls, fetch, next/headers) rather than trying to render them in a browser environment.
next/navigation mocking: useRouter, usePathname, useSearchParams, and redirect are not available in the test environment. Always mock next/navigation. For complex navigation testing, use the next-router-mock package which provides a mock router with state tracking.
MSW integration: MSW intercepts fetch() at the network layer — no need to mock fetch globally. Set up server.listen() in beforeAll, server.resetHandlers() in afterEach, and server.close() in afterAll. Use server.use() within a test to override handlers for specific scenarios.
Vitest vs Jest: Vitest is faster and has native ESM support — preferred for new Next.js projects. Configure vitest.config.ts with @vitejs/plugin-react and alias next/navigation to a mock. Jest requires next/jest transform configuration for App Router compatibility.
next/cache in tests: revalidatePath and revalidateTag throw errors in test environments. Mock next/cache module entirely: vi.mock('next/cache', () => ({ revalidatePath: vi.fn(), revalidateTag: vi.fn() })).
E2E testing: For full integration tests including navigation and streaming, use Playwright with next dev or next start. Playwright can test App Router features (streaming, client navigation) that JSDOM-based unit tests cannot.
https://nextjs.org/docs/app/building-your-application/testing
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeDetects React project structure (Next.js, Vite, CRA) and configures jest/vitest, @testing-library, MSW, customRender with provider context, and test scripts.
Provides testing patterns and examples for React components, hooks, and integrations using Vitest, React Testing Library, and Jest.
Guides Vitest test writing with mocking, MSW v2 HTTP mocking, snapshot testing, and test infrastructure. Use when writing or debugging Vitest tests.