API Integration Tests
Checks API behavior across routing, validation, persistence, and response boundaries with integration tests.
Scenario
You maintain a Node.js API where unit tests cover services, but important endpoint behavior is verified with integration tests.
Integration tests exercise multiple parts of the application together:
HTTP request
↓
routing
↓
validation
↓
service
↓
repository
↓
test database
The team wants agents to understand when endpoint-level integration tests are needed and how to keep them reliable.
Repository Structure
api/
├── src/
│ ├── routes/
│ ├── services/
│ ├── repositories/
│ └── schemas/
├── tests/
│ ├── unit/
│ └── integration/
│ ├── users.test.ts
│ └── orders.test.ts
├── test/
│ ├── setup-database.ts
│ └── factories.ts
└── AGENTS.md
AGENTS.md
# Project Instructions
## API Integration Tests
Add or update integration tests when changes affect:
- endpoint contracts;
- request validation;
- authentication or authorization behavior;
- persistence behavior visible through the API;
- HTTP status codes or response bodies.
## Test Environment
- Use the existing test database setup.
- Use repository test factories when creating test data.
- Keep each test independent of execution order.
- Clean up or reset state using the existing test utilities.
## Assertions
- Assert externally observable API behavior.
- Verify important status codes and response fields.
- Avoid asserting internal service or repository calls in API integration tests.
## External Services
- Do not call real third-party services from integration tests unless the repository explicitly supports that workflow.
- Use the existing test doubles or sandbox integrations.
## Validation
Run the affected integration suite after API changes.
For broader API changes, also run:
- `pnpm lint`
- `pnpm typecheck`
- `pnpm test`
What This Does
This defines the role of integration tests.
Suppose an endpoint is:
POST /orders
A useful integration test might verify:
valid request
↓
201 response
↓
expected response shape
↓
order persisted
Another test might verify:
invalid request
↓
400 response
↓
expected validation error
These tests validate how several application layers work together.
What This Does NOT Do
Integration tests do not need to verify every internal method call.
For example:
expect(orderService.create).toHaveBeenCalled();
is usually not the main concern of an HTTP integration test.
The observable result is more important:
expect(response.status).toBe(201);
expect(response.body.id).toBeDefined();
Likewise, integration tests should not depend on another test having already created data.
Tests that depend on execution order become difficult to run individually and can fail unpredictably.
Why These Instructions Matter
Unit tests may show that:
validation works
service works
repository works
individually.
But the endpoint can still fail because those pieces are wired together incorrectly.
For example:
schema expects `email`
controller reads `emailAddress`
Individual unit tests might miss the mismatch.
An integration test exercising the actual HTTP request can catch it.
Database state is another source of problems.
If tests share state, this can happen:
Test A creates user
↓
Test B assumes user exists
↓
Test B passes only when A runs first
The instruction:
- Keep each test independent of execution order.
helps prevent that coupling.
Key Decisions
Integration tests verify boundaries working together
Use them where the interaction between components matters.
Use realistic infrastructure selectively
A test database can provide confidence in persistence behavior.
Real production third-party APIs usually make tests slower and less deterministic, so controlled test doubles or sandbox environments are often preferable.
Assert external behavior
HTTP tests should primarily care about:
request
response
persistent outcome
rather than internal call structure.
Keep tests independent
Each test should establish the state it needs.
That makes failures easier to reproduce.
When to Use This Pattern
Use this pattern when:
- API contracts are important;
- validation and persistence need end-to-end verification at the service boundary;
- unit tests alone cannot verify application wiring;
- authentication or authorization affects endpoints;
- database behavior is part of the observable result.
Integration tests provide confidence between isolated unit tests and full browser-level E2E tests.