Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Write integration tests for key workflows

Test how multiple units of code work together — API routes with their database queries, form submissions with validation, and component trees with their state management.

Utilities
Quick take
Typical fix time 30 min
  • Integration tests verify that multiple units work correctly together
  • Test at the boundary where different parts of your system meet
  • Use a real database in tests (test DB or in-memory) rather than mocking everything
  • Test happy paths and the most critical error paths
Why it matters: Unit tests verify individual functions in isolation, but they can't catch the bugs that occur when those functions interact — a perfectly correct validation function and a correct database function can still fail when wired together incorrectly. Integration tests catch these wiring bugs before they reach production, where they're expensive to diagnose.

Rule Details

Integration tests verify that separate units of code work correctly when connected together, catching the bugs that unit tests can't.

Code Example

// test/api/users.integration.test.js
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import supertest from 'supertest'
import { app } from '../src/app.js'
import { db } from '../src/db.js'
 
describe('POST /api/users', () => {
  beforeAll(async () => {
    await db.migrate.latest()
    await db.seed.run()
  })
 
  afterAll(async () => {
    await db.destroy()
  })
 
  it('creates a user with valid data', async () => {
    const response = await supertest(app)
      .post('/api/users')
      .send({ name: 'Alice', email: 'alice@example.com' })
      .expect(201)
 
    expect(response.body).toMatchObject({
      id: expect.any(Number),
      name: 'Alice',
      email: 'alice@example.com'
    })
 
    // Verify it was actually saved
    const saved = await db('users').where({ email: 'alice@example.com' }).first()
    expect(saved).toBeDefined()
  })
 
  it('returns 400 for duplicate email', async () => {
    await supertest(app)
      .post('/api/users')
      .send({ name: 'Bob', email: 'existing@example.com' })
      .expect(400)
  })
})

Why It Matters

Unit tests verify individual functions in isolation, but they can't catch the bugs that occur when those functions interact — a perfectly correct validation function and a correct database function can still fail when wired together incorrectly. Integration tests catch these wiring bugs before they reach production, where they're expensive to diagnose.

Component Integration with React Testing Library

// test/UserProfileForm.integration.test.jsx
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { QueryClientProvider } from '@tanstack/react-query'
import { UserProfileForm } from '../src/components/UserProfileForm'
import { server } from './mocks/server'
import { rest } from 'msw'
 
describe('UserProfileForm', () => {
  it('submits updated profile and shows success message', async () => {
    const user = userEvent.setup()
 
    render(
      <QueryClientProvider client={queryClient}>
        <UserProfileForm userId={1} />
      </QueryClientProvider>
    )
 
    // Fill in the form
    const nameInput = screen.getByLabelText('Full name')
    await user.clear(nameInput)
    await user.type(nameInput, 'Alice Updated')
 
    // Submit
    await user.click(screen.getByRole('button', { name: 'Save changes' }))
 
    // Verify success feedback (tests form + API call + state update + UI render)
    await waitFor(() => {
      expect(screen.getByText('Profile saved successfully')).toBeInTheDocument()
    })
  })
 
  it('shows field errors when API returns validation failure', async () => {
    server.use(
      rest.put('/api/users/:id', (req, res, ctx) =>
        res(ctx.status(422), ctx.json({ errors: { name: 'Name is too long' } }))
      )
    )
 
    // ... test error display
  })
})

What to Integration Test

High-value integration test targets:
✓ API routes + database queries (the full server-side stack)
✓ Form components + submission + API response + UI feedback
✓ Authentication flows (login → session → protected route)
✓ Shopping cart + checkout + order creation
✓ File upload + processing + storage + thumbnail display
✓ Search input + query → results display
 
Skip (better as unit tests):
✗ Pure utility functions
✗ Simple transformations
✗ Components with no external dependencies

Standards

  • Use these references as the standard for how the test or monitoring strategy should behave in the shipped workflow.
  • Check the implementation against Playwright Docs before treating the rule as satisfied.
  • Check the implementation against Testing Library Guiding Principles before treating the rule as satisfied.

Verification

  1. Run the relevant test or CI step locally and confirm it fails when the rule is violated.
  2. Ensure the automation blocks regressions instead of only printing warnings.
  3. Cover at least one representative high-risk flow, component, or route.
  4. Keep thresholds or assertions in version control so changes remain reviewable.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Identify the key integration points in this codebase — where API routes call services, where services interact with the database, and where components interact with state management. Are these tested?

Fix

Auto-fix issues

Write integration tests for the identified critical paths, testing the interactions between components rather than each in isolation.

Explain

Learn more

Explain integration testing, how it differs from unit and E2E testing, and what tools to use for API and component integration tests.

Review

Code review

Review tests, CI workflows, and enforcement points related to Write integration tests for key workflows. Flag exact gaps where the rule is not automatically verified or where failures do not block regressions.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Rules that often go hand-in-hand with this one.

Write unit tests

Critical functionality has unit tests with good coverage for reliability.

Testing
Implement end-to-end testing

Use E2E testing frameworks like Playwright or Cypress to test critical user journeys.

Testing
Implement consumer-driven contract testing for API boundaries

Consumer-driven contract tests (Pact) define and verify the API contracts between the frontend consumer and backend provider, catching integration mismatches before they reach production.

Testing
Use mutation testing to measure how well tests detect bugs

Run Stryker mutation testing on critical business logic to verify that your test suite will actually catch real bugs, not just achieve line coverage.

Testing

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385