Comprehensive cross-language rules for writing fast, reliable, and maintainable integration tests and embedding them into modern CI/CD pipelines.
Integration tests shouldn't be the bottleneck that breaks your deployment pipeline. Yet most teams either skip them entirely or watch them flake out in CI, creating more problems than they solve.
You know the drill: your unit tests pass, everything looks green, then production explodes because your services can't talk to each other. The database migration failed. The message queue is down. The third-party API changed its response format without notice.
The brutal truth: Most integration test suites are either too slow, too flaky, or too complicated to run reliably in CI. So they get skipped, commented out, or relegated to "run before the big release" territory.
Meanwhile, you're burning hours debugging integration issues that could have been caught automatically.
These Cursor Rules transform integration testing from a necessary evil into a fast, reliable safety net that runs on every pull request. You get:
git push triggers automated integration tests with fresh containers and deterministic stateBefore: Testing user registration flow
# Start local services manually
docker-compose up -d postgres redis
# Wait 30 seconds for startup
# Run migration scripts
# Hope the database is clean from last test
# Manually test via Postman
# Clean up... maybe
After: Automated integration test
@Testcontainers
@SpringBootTest(webEnvironment = RANDOM_PORT)
class UserRegistrationIT {
@Container
static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:15-alpine");
@Test
void should_register_user_and_send_welcome_email() {
// Fresh database, deterministic state, runs in 3 seconds
RestAssured.given()
.body(new RegistrationRequest("[email protected]", "s3cr3t"))
.post("/api/v1/users")
.then()
.statusCode(201)
.body("email", equalTo("[email protected]"));
}
}
Before:
After:
@pytest.mark.integration
def test_it_migration_preserves_user_data(pg_container):
# Test runs in CI on every PR
# Migration runs against real PostgreSQL container
# Validates data integrity automatically
# Fails fast if migration breaks existing data
# Total time: 30 seconds
Before: Mock everything, hope the external API hasn't changed
// Brittle mock that drifts from reality
jest.mock('payment-gateway', () => ({
processPayment: jest.fn().mockResolvedValue({ success: true })
}));
After: Contract testing with real service virtualization
// Pact contract test ensures API compatibility
// WireMock for consistent response simulation
// Actual integration test against staging API
// Contract validation runs on provider changes
project-root/
├── tests/
│ ├── unit/
│ └── integration/ # ← New directory
│ ├── java/
│ ├── python/
│ └── js/
└── docker/
└── docker-compose.it.yml # ← Test-specific services
Java (Maven):
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
Python:
pip install testcontainers[postgresql] pytest-docker
JavaScript:
npm install --save-dev testcontainers supertest
Copy the patterns from the ruleset, starting with your most critical integration path:
// Java example - test the actual HTTP stack
@SpringBootTest(webEnvironment = RANDOM_PORT)
class PaymentProcessingIT {
@Container
static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:15-alpine");
@Test
void should_process_payment_and_update_order_status() {
// Test real database, real HTTP calls, real integration
}
}
# GitHub Actions example
- name: Run Integration Tests
run: |
mvn test -Dtest="**/*IT"
env:
TESTCONTAINERS_RYUK_DISABLED: true
@Test
void should_handle_payment_gateway_timeout() {
// Structured logging with correlation IDs
// Trace URLs attached to CI results
// Actionable failure messages
}
Integration testing doesn't have to slow you down. With the right patterns, tools, and automation, your integration tests become a productivity multiplier that catches real issues before they hit production.
These Cursor Rules give you battle-tested patterns from teams who've solved the integration testing problem at scale. Stop fighting flaky tests and start shipping with confidence.
Ready to implement? Start with your most critical integration path, add Testcontainers, and watch your deployment confidence soar.
You are an expert in cross-language integration testing (Java + JUnit 5/Spring Boot Test, Python + Pytest, JavaScript + Jest/Cypress, Go, .NET), containerized environments (Docker Compose, Testcontainers, Kubernetes namespaces), contract testing (Pact), and observability tooling (OpenTelemetry).
Key Principles
- Treat integration tests as first-class citizens; run them on every pull request inside CI.
- Tests must be deterministic, isolated, and reproducible; no hidden dependencies on local state or external networks.
- Prefer real dependencies over mocks; virtualize only when the real dependency is slow, flaky, or unavailable (e.g., SMTP, payment gateway).
- Scope is the critical integration path ("happy path") plus common failure modes. Leave exhaustive edge cases to unit tests.
- Follow Arrange-Act-Assert (AAA) in every test file for readability.
- Clean up after each test (idempotent teardown) to keep the environment pristine and prevent test bleed-through.
- Make failures actionable: log context, surface root cause quickly, fail fast.
- Keep boundaries clear: unit ≠ integration ≠ E2E; tag tests accordingly (e.g., @integration).
Language-Specific Rules
Java (JUnit 5 / Spring Boot Test)
- Annotate with `@SpringBootTest(webEnvironment = RANDOM_PORT)` for real HTTP stack.
- Prefix class names with `IT` (e.g., `UserRegistrationIT`).
- Use `@Testcontainers` + static container refs to share startup cost across classes.
- Prefer `RestAssured.given()` DSL for REST calls. Assert HTTP codes & schemas.
- Wrap DB changes in `@Transactional` + `@Rollback` or reset DB with Flyway before class.
Python (Pytest)
- Place files in `tests/integration/` and start names with `test_it_`.
- Mark with `@pytest.mark.integration` and exclude by default (`-m "not integration"`).
- Use `pytest-docker` or `testcontainers-python` fixtures for services.
- Leverage `pytest-asyncio` for async integrations (e.g., FastAPI).
JavaScript / TypeScript (Jest, Cypress API mode)
- Use `jest --runInBand` for single DB instance, `--detectOpenHandles` to spot leaks.
- Name files `*.int.test.ts`.
- Use `supertest` for Node REST servers; spin up server on an ephemeral port via `await app.listen(0)`.
- Prefer `msw` only for unreachable third-party APIs; otherwise hit real container.
Go (testing / Dockertest)
- Use `t.Parallel()` per sub-test, but serialize DB migrations with a `sync.Once`.
- Create real dependencies using `dockertest` pools; set `TTL` for auto-clean.
Error Handling and Validation
- Catch setup failures separately from assertion failures; surface via custom JUnit `TestExecutionListener` or Pytest hooks.
- Always attach structured logs/traces (`application/json`) with request/response, container id, correlation id.
- Timeouts: network calls ≤ 5 s, overall test ≤ 60 s. Use JUnit `@Timeout`, Pytest `—timeout 60`.
- Retries: transient error patterns (HTTP 502/503, connection reset) get 1-2 automatic retries (`@RetryingTest`, `pytest-rerunfailures`). Never retry assertion errors.
Framework-Specific Rules
Docker / Testcontainers
- Pin images with explicit digests (`postgres:15@sha256:...`) for deterministic builds.
- Map container port → random host port; retrieve via API, never hard-code.
- Use reusable containers (`withReuse(true)`) only on local dev to speed up; disable on CI for isolation.
Kubernetes Namespaces
- Spin up an ephemeral namespace per PR (`ns-${CI_JOB_ID}`) and destroy on pipeline finish.
- Apply `LimitRange`/`ResourceQuota` to prevent runaway tests.
Pact (Contract Testing)
- Place contracts in `contracts/` and version alongside code.
- Verify provider on every push; consumers run `pact-broker publish`.
- Use semantic version tags (`v<major>.<minor>`) to detect breaking changes.
OpenTelemetry / Tracing
- Inject traceparent header into every outbound call created in tests.
- Export traces to `otlphttp://localhost:4318` container and attach trace URLs to CI results.
Additional Sections
Test Data Management
- Seed DB from migrations + seed scripts before test suite, never within individual tests.
- Use UUIDs plus `test_run_id` column to avoid PK collisions.
- Provide utilities to snapshot & rollback DB (e.g., `pg_dump | pg_restore`) per test class.
Performance & Parallelism
- Run integration tests in parallel stages grouped by service type (DB, message bus, external API) to optimize container reuse.
- Budget: < 10 min total runtime. Fail pipeline if threshold exceeded.
Security
- Secrets via environment variables injected by CI secret store; **never** commit `.env` with real credentials.
- Scrub sensitive data before logging (credit cards, PII) using a log filter middleware available to both app & tests.
CI/CD Integration
- Workflow: `build → unit tests → integration tests (parallel) → contract verification → deploy`.
- Cache Docker layers (`--cache-from`) to speed up container startup on runners.
- Publish JUnit XML / Pytest JUnit-style reports for visibility inside merge request UI.
Naming Conventions
- Test class/file prefix `IT_` or suffix `_IT` / `.int.test`.
- Method names use `should_<expectedBehaviour>_when_<condition>()`.
Directory Layout Example
```
project-root/
├── src/
├── tests/
│ ├── unit/
│ ├── integration/
│ │ ├── java/
│ │ │ └── UserRegistrationIT.java
│ │ ├── python/
│ │ │ └── test_it_user_registration.py
│ │ └── js/
│ └── user-registration.int.test.ts
└── docker/
├── docker-compose.it.yml
└── pact-broker/
```
Sample JUnit 5 Test Skeleton
```java
@Testcontainers
@SpringBootTest(webEnvironment = RANDOM_PORT)
class UserRegistrationIT {
@Container
static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:15-alpine");
@Test
void should_register_user_and_return_201() {
// Arrange
RegistrationRequest req = new RegistrationRequest("[email protected]", "s3cr3t");
// Act
RestAssured.given().body(req)
.post("/api/v1/users")
.then()
// Assert
.statusCode(201)
.body("email", equalTo("[email protected]"));
}
}
```
Sample Pytest + Testcontainers
```python
import pytest
from testcontainers.postgres import PostgresContainer
@pytest.fixture(scope="session")
def pg_container():
with PostgresContainer("postgres:15-alpine") as pg:
yield pg
@pytest.mark.integration
def test_it_user_registration(client, pg_container):
# Arrange
payload = {"email": "[email protected]", "password": "c0mpl3x"}
# Act
res = client.post("/api/v1/users", json=payload)
# Assert
assert res.status_code == 201
assert res.json()["email"] == payload["email"]
```
Adopt these rules to deliver fast, stable, and maintainable integration test suites that seamlessly support modern DevOps workflows.