Testing API routes in Next.js is essential for building reliable applications. Writing tests manually takes time, but AI tools can accelerate the process significantly. This guide shows you how to use AI to generate Jest tests for your Next.js API routes efficiently.
Setting Up Jest for Next.js API Routes
Before generating tests, ensure your Next.js project has Jest configured properly. Next.js 13 and later versions include Jest support out of the box, but you may need to install additional packages for API route testing.
npm install --save-dev jest @testing-library/react @testing-library/jest-dom
npm install --save-dev jest-environment-node
Create a Jest configuration file at your project root:
// jest.config.js
const nextJest = require('next/jest')
const createJestConfig = nextJest({
dir: './',
})
const customJestConfig = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testEnvironment: 'jest-environment-node',
}
module.exports = createJestConfig(customJestConfig)
Create your setup file to include testing utilities:
// jest.setup.js
import '@testing-library/jest-dom'
Using AI to Generate Test Cases
When you have an API route that needs testing, provide the AI tool with your route code and request specific test scenarios. The key is giving the AI enough context about what your route does.
Example: Testing a Simple API Route
Consider this Next.js API route that handles user registration:
// pages/api/register.js
import { NextResponse } from 'next/server'
export async function POST(request) {
try {
const { email, password } = await request.json()
if (!email || !password) {
return NextResponse.json(
{ error: 'Email and password are required' },
{ status: 400 }
)
}
if (password.length < 8) {
return NextResponse.json(
{ error: 'Password must be at least 8 characters' },
{ status: 400 }
)
}
// Simulate user creation (replace with actual database logic)
const user = { id: '1', email }
return NextResponse.json({ user }, { status: 201 })
} catch (error) {
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
Ask your AI tool to generate Jest tests for this route. A good prompt includes the route code and specifies what behaviors to test.
Generated Test Structure
The AI should produce tests covering various scenarios:
// __tests__/api/register.test.js
import { POST } from '@/pages/api/register'
import { NextRequest } from 'next/server'
// Helper to create mock requests
function createMockRequest(body, options = {}) {
return {
json: async () => body,
...options,
}
}
describe('POST /api/register', () => {
it('should create a new user with valid email and password', async () => {
const request = createMockRequest({
email: 'test@example.com',
password: 'password123',
})
const response = await POST(request)
const data = await response.json()
expect(response.status).toBe(201)
expect(data.user).toHaveProperty('id')
expect(data.user.email).toBe('test@example.com')
})
it('should return 400 if email is missing', async () => {
const request = createMockRequest({
password: 'password123',
})
const response = await POST(request)
const data = await response.json()
expect(response.status).toBe(400)
expect(data.error).toBe('Email and password are required')
})
it('should return 400 if password is missing', async () => {
const request = createMockRequest({
email: 'test@example.com',
})
const response = await POST(request)
const data = await response.json()
expect(response.status).toBe(400)
expect(data.error).toBe('Email and password are required')
})
it('should return 400 if password is too short', async () => {
const request = createMockRequest({
email: 'test@example.com',
password: 'short',
})
const response = await POST(request)
const data = await response.json()
expect(response.status).toBe(400)
expect(data.error).toBe('Password must be at least 8 characters')
})
it('should return 500 on internal server error', async () => {
// Test error handling by passing invalid JSON
const request = {
json: async () => {
throw new Error('Database connection failed')
},
}
const response = await POST(request)
const data = await response.json()
expect(response.status).toBe(500)
expect(data.error).toBe('Internal server error')
})
})
Testing More Complex API Routes
For API routes that interact with databases or external services, use mocking to isolate your tests. The AI can help generate appropriate mock setups.
// pages/api/users/[id].js
import { NextResponse } from 'next/server'
export async function GET(request, { params }) {
const { id } = params
// Simulate database fetch
if (id === '999') {
return NextResponse.json(
{ error: 'User not found' },
{ status: 404 }
)
}
const user = {
id,
name: 'John Doe',
email: 'john@example.com',
role: 'user',
}
return NextResponse.json({ user })
}
export async function PUT(request, { params }) {
const { id } = params
const updates = await request.json()
if (!updates.name && !updates.email) {
return NextResponse.json(
{ error: 'No fields to update' },
{ status: 400 }
)
}
return NextResponse.json({
user: { id, ...updates },
message: 'User updated successfully',
})
}
The corresponding tests would cover GET and PUT operations with various scenarios:
// __tests__/api/users/[id].test.js
import { GET, PUT } from '@/pages/api/users/[id]'
describe('GET /api/users/[id]', () => {
it('should return user data for valid ID', async () => {
const request = {}
const params = { id: '1' }
const response = await GET(request, { params })
const data = await response.json()
expect(response.status).toBe(200)
expect(data.user).toEqual({
id: '1',
name: 'John Doe',
email: 'john@example.com',
role: 'user',
})
})
it('should return 404 for non-existent user', async () => {
const request = {}
const params = { id: '999' }
const response = await GET(request, { params })
const data = await response.json()
expect(response.status).toBe(404)
expect(data.error).toBe('User not found')
})
})
describe('PUT /api/users/[id]', () => {
it('should update user with provided fields', async () => {
const request = {
json: async () => ({ name: 'Jane Doe' }),
}
const params = { id: '1' }
const response = await PUT(request, { params })
const data = await response.json()
expect(response.status).toBe(200)
expect(data.user.name).toBe('Jane Doe')
expect(data.message).toBe('User updated successfully')
})
it('should return 400 when no fields provided', async () => {
const request = {
json: async () => ({}),
}
const params = { id: '1' }
const response = await PUT(request, { params })
const data = await response.json()
expect(response.status).toBe(400)
expect(data.error).toBe('No fields to update')
})
})
Best Practices for AI-Generated Tests
AI-generated tests provide a solid foundation, but review them carefully. Verify that edge cases are covered and that the tests match your actual implementation. Add integration tests for routes that interact with real databases or external APIs.
Run your tests frequently during development:
npm test -- --coverage
This shows you which parts of your API routes remain untested, helping you identify gaps in your test coverage.
Testing Authentication and Authorization in API Routes
Most production Next.js API routes require authentication. AI can generate tests that verify protected routes reject unauthenticated requests and enforce role-based access.
Provide your auth middleware and route code together when prompting the AI, so it understands the entire request lifecycle:
// pages/api/admin/users.js
import { getServerSession } from 'next-auth'
import { NextResponse } from 'next/server'
export async function GET(request) {
const session = await getServerSession()
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
if (session.user.role !== 'admin') {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
}
const users = [{ id: '1', email: 'user@example.com' }]
return NextResponse.json({ users })
}
The AI generates tests that mock the session appropriately:
// __tests__/api/admin/users.test.js
import { GET } from '@/pages/api/admin/users'
import { getServerSession } from 'next-auth'
jest.mock('next-auth', () => ({
getServerSession: jest.fn(),
}))
describe('GET /api/admin/users', () => {
it('should return 401 when not authenticated', async () => {
getServerSession.mockResolvedValue(null)
const response = await GET({})
expect(response.status).toBe(401)
})
it('should return 403 when user is not admin', async () => {
getServerSession.mockResolvedValue({ user: { role: 'viewer' } })
const response = await GET({})
expect(response.status).toBe(403)
})
it('should return user list for admin role', async () => {
getServerSession.mockResolvedValue({ user: { role: 'admin' } })
const response = await GET({})
const data = await response.json()
expect(response.status).toBe(200)
expect(data.users).toHaveLength(1)
})
})
Always verify that your auth tests cover all role combinations, not just the happy path. Missed role checks in tests often mean missed role checks in the actual routes.
Testing API Routes with Database Interactions
When your API routes read from or write to a database, mock the database layer so tests run in isolation without a real DB connection. A practical pattern uses Jest module mocking to intercept Prisma or similar ORM calls:
// __tests__/api/posts/[id].test.js
import { GET } from '@/pages/api/posts/[id]'
import { prisma } from '@/lib/prisma'
jest.mock('@/lib/prisma', () => ({
prisma: {
post: {
findUnique: jest.fn(),
},
},
}))
describe('GET /api/posts/[id]', () => {
it('should return a post when found', async () => {
const mockPost = { id: 1, title: 'Test Post', body: 'Content' }
prisma.post.findUnique.mockResolvedValue(mockPost)
const response = await GET({}, { params: { id: '1' } })
const data = await response.json()
expect(response.status).toBe(200)
expect(data.post.title).toBe('Test Post')
})
it('should return 404 when post does not exist', async () => {
prisma.post.findUnique.mockResolvedValue(null)
const response = await GET({}, { params: { id: '999' } })
const data = await response.json()
expect(response.status).toBe(404)
expect(data.error).toBe('Post not found')
})
})
When prompting AI to generate database-backed route tests, include your Prisma schema or data model in the prompt. This context lets the AI generate accurate mock return values that match your real data shapes.
Structuring AI Prompts for Better Test Output
The quality of AI-generated tests depends significantly on how you frame the prompt. Vague prompts produce generic tests; specific prompts produce immediately useful ones.
Effective prompt structure for Next.js API route tests:
- Paste the complete route handler code
- Specify which scenarios matter most: validation errors, auth checks, DB errors
- Mention your mocking approach (jest.mock, manual mocks, or MSW)
- Ask for descriptions that explain the business rule being verified, not the HTTP status
This level of specificity typically cuts the time spent editing AI-generated tests from 30 minutes to under 10 minutes per route. The AI produces tests closer to production-ready because it understands your specific constraints upfront rather than guessing at them.
Related Articles
- Writing Effective .cursorrules for Next.js App Router
- How to Use AI to Generate Jest Component Tests with Testing
- How to Use AI to Generate Jest Integration Tests for Express
- How to Use AI to Generate Jest Tests for
- How to Use AI to Generate Jest Tests for Redux Toolkit Slice
Built by theluckystrike — More at zovo.one