Careeroza
Testing Actions and Route Handlers
Route Handlers Are Functions
Import the handler, pass a Request, and check the Response. Mock the data layer so no database is needed.
// app/api/products/[id]/route.test.ts
import { describe, expect, it, vi } from 'vitest';
vi.mock('@/lib/data/products', () => ({
getProduct: vi.fn(async (id: number) => (id === 1 ? { id: 1, name: 'Clean Code', price: 450 } : null)),
}));
import { GET } from './route';
describe('GET /api/products/[id]', () => {
it('returns 404 for a missing product', async () => {
const res = await GET(new Request('http://test/api/products/9'), { params: Promise.resolve({ id: '9' }) });
expect(res.status).toBe(404);
});
});Server Actions
A Server Action is an async function; call it with a FormData. Mock auth(), revalidatePath and redirect:
vi.mock('@/auth', () => ({ auth: vi.fn(async () => ({ user: { id: 'u1', role: 'admin' } })) }));
vi.mock('next/cache', () => ({ revalidatePath: vi.fn() }));
import { createProduct } from './actions';
it('returns field errors for bad input', async () => {
const fd = new FormData();
fd.set('name', 'TV');
fd.set('price', '-1');
const state = await createProduct({}, fd);
expect(state.errors?.price?.[0]).toBe('Price must be more than 0');
});