Integration Testing skill for Codex

Test the seams where production actually breaks. Codex writes integration tests around real boundaries such as SQL, transactions, framework wiring, queue consumers and outbound clients, using the real engine for what you own and HTTP stubs for what you do not. It picks a database isolation strategy that fits the code (rollback, truncation, per-worker template databases), covers duplicate delivery and partial failure, and proves each test fails when its boundary breaks.

  • Testing & QA
  • $integration-testing
  • 130 lines
  • 2 files
  • ~1,750 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- integration-testing

Installs to ~/.agents/skills/integration-testing/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $integration-testing, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Designs integration tests that exercise real seams - HTTP handlers through the database, repositories against the real engine, queue consumers, external API clients - choosing what is real and what is faked, isolating database state per test or per worker (transaction rollback, truncation, template databases), running migrations once, and keeping suites parallel and fast. Use when unit tests pass but production breaks at the boundaries, when deciding what an integration test should cover, when tests share or leak database state, or when an integration suite is slow or order-dependent.

Use it when

  • Unit tests pass but bugs appear at database, HTTP or queue boundaries
  • Integration tests share state, depend on order or cannot run in parallel
  • Deciding the scope of a new integration test
  • Framework transaction wrappers hide commit-time behaviour

Use something else when

  • You need container setup details for a specific language: use testcontainers
  • You are testing a deployed HTTP API from the outside: use api-testing
  • You are verifying another team's API contract: use contract-testing

How to check the result

The suite passes twice in random order at CI parallelism, and dropping the index, check or field each test protects makes that test fail.

The mistake it prevents

Relying on per-test transaction rollback while the code under test commits on its own connection, so tests see nothing or skip the commit-time constraints they were meant to check.

~/.agents/skills/integration-testing/SKILL.md

--- name: integration-testing description: Designs integration tests that exercise real seams - HTTP handlers through the database, repositories against the real engine, queue consumers,… license: MIT ---

An integration test is worth its cost when it crosses a boundary where bugs actually live (SQL, serialisation, transactions, framework wiring, configuration), uses the real thing on our side of that boundary, fakes only what we do not own, and leaves no state behind for the next test.

Inspect first

  1. Read AGENTS.md for the test commands and how integration tests are separated (folder, tag, file suffix such as *IT.java, *.int.test.ts, -m integration).
  2. List the system’s boundaries: database, cache, queue, object storage, third-party HTTP APIs, filesystem, clock, email/SMS.
  3. Read the existing test setup: how the DB is created, how state is reset, whether tests run in parallel, how long the suite takes.
  4. Check recent production incidents or bug tickets: integration tests should cover the classes of failure that actually happened.

Decide what is real

DependencyIn integration testsWhy
Your databaseReal engine and version (container or CI service)SQL dialect, constraints, isolation and JSON behaviour differ in fakes
Your cache, queue, search engineReal, containerisedSerialisation, TTLs, ordering, acknowledgements
Your framework (routing, DI, middleware, auth)Real, in-processWiring and config bugs are the target
Another team’s serviceStub at HTTP (MSW, WireMock, respx) plus contract testsYou cannot control its data or uptime
Third-party SaaS (Stripe, Twilio, S3)Stub at HTTP or official local emulator (LocalStack, stripe-mock)Cost, rate limits, side effects
Clock, randomnessInjected and controlledDeterminism
Email, SMS, pushIn-memory adapter or local catcher (Mailpit)Never send real messages from tests

Use testcontainers for container plumbing and api-mocking-msw for HTTP stubs.

Pick the slice

SliceCoversExample
Repository / DAOQueries, mappings, constraints, migrationsOrderRepository.upsert keeps one row per reference under conflict
Service + DBTransactions, rollback on error, lockingFailed payment leaves stock unchanged
HTTP in-processRouting, validation, auth middleware, serialisation, error mappingPOST /orders without a token returns 401 problem+json
ConsumerDeserialisation, idempotency, retry/dead-letter behaviourSame message twice produces one order
Outbound clientRequest shape, timeouts, error mappingProvider 429 becomes a retryable error with backoff

Test the HTTP layer in process (supertest, FastAPI TestClient/httpx ASGITransport, Spring MockMvc or WebTestClient, Go httptest.NewServer) rather than over a deployed URL; that is api-testing or E2E territory.

Database isolation strategies

StrategySpeedWorks whenBreaks when
Wrap each test in a transaction, roll backFastestCode under test uses the same connectionCode opens its own transactions/connections, uses COMMIT-time behaviour (deferred constraints, triggers on commit, after-commit hooks, LISTEN/NOTIFY)
Truncate touched tables after each testFastSmall schemasMany tables; parallel tests sharing a DB
Database per worker from a templateFast and parallelPostgres (CREATE DATABASE w1 TEMPLATE app_template)Engines without cheap cloning
Schema per workerParallelApps that honour search_path / schema configCode hard-codes schema names
Unique data per test, never cleanParallel, simpleAppend-only or tenant-scoped domainsTests that count all rows

Default for Postgres: run migrations once into app_template, then create one database per worker from it, and roll back or truncate within the worker.

-- once per run, after migrations (marking it a template lets non-owners clone it)
ALTER DATABASE app_template IS_TEMPLATE true;
-- per worker (no active connections to the template allowed)
CREATE DATABASE app_w3 TEMPLATE app_template;

Framework defaults to know: Django TestCase wraps each test in a transaction (use TransactionTestCase when the code commits or uses on_commit; or captureOnCommitCallbacks); Rails transactional tests roll back by default; Spring @Transactional on a test rolls back but hides lazy-loading and commit-time errors.

Workflow

  • Write down the boundary and the failure the test protects against
  • Choose the slice and what is real vs stubbed using the tables above
  • Set up data with factories inside the test (see test-data-factories), not shared fixtures
  • Act through the public entry point of the slice (HTTP call, service method, message)
  • Assert on externally visible results: response, persisted rows, emitted messages, outbound requests; not on internal method calls
  • Include the unhappy paths that matter: constraint violation, timeout, duplicate delivery, partial failure, permission denied
  • Run the new tests in random order and in parallel before committing
def test_duplicate_payment_webhook_is_processed_once(client, db, stripe_signature):
    order = OrderFactory(status="pending", payment_intent="pi_123")
    payload = webhook_payload("payment_intent.succeeded", "pi_123")

    for _ in range(2):
        r = client.post("/webhooks/stripe", content=payload,
                        headers={"Stripe-Signature": stripe_signature(payload)})
        assert r.status_code == 200

    db.refresh(order)
    assert order.status == "paid"
    assert db.query(LedgerEntry).filter_by(order_id=order.id).count() == 1

Keeping the suite fast

  • Start containers and run migrations once per run, not per file.
  • Parallelise by worker with isolated databases; target a suite under 5-10 minutes in CI.
  • Keep integration tests focused: one boundary each. Business-rule permutations belong in unit tests below.
  • Tag integration tests so developers can run unit tests alone in seconds.

Verification

  • Run the suite twice, in random order, with the CI worker count. All green both times.
  • Break the thing each test claims to protect (drop a unique index, remove the idempotency check, change a JSON field name) and confirm a test fails.
  • Confirm no test depends on data created by another (rg for hard-coded ids; random order catches the rest).

Gotchas

  • Transaction-rollback isolation silently skips commit-time behaviour; test the commit-dependent paths with truncation instead.
  • Asserting on count(*) of a whole table fails as soon as tests run in parallel; scope queries to the data the test created.
  • Seeding a big shared dataset “for realism” creates coupling; each test creates its own.
  • Stubbing your own database layer in an “integration” test turns it into a slow unit test.
  • Tests that pass only with a specific TZ or locale on CI: set them explicitly in the test environment, matching production.

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Integration Testing"
  short_description: "Test the seams where production actually breaks"
  default_prompt: "$integration-testing Write integration tests for our order service that hit a real Postgres"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc