Should I test that?

Should integration tests run in CI?

Verdict

Yes

Yes, run integration tests in CI on every pull request, against the production database engine in a service container, and block the merge when one fails; move only the tests that call a third-party sandbox to a scheduled run.

Why

Yes, run integration tests in CI on every pull request; rule R11 gives Test for the code they cover. The typical case is a backend service whose integration tests run on laptops or nightly, while pull requests run only unit tests. Blast radius is users, and Change frequency is regularly, because most features change a query or a mapping. Detectability is eventually, because a wrong filter returns plausible rows without an error, and Reversibility is with-effort, because a script repairs the rows code acted on. Test cost is moderate: the tests exist, and a CI job with a database container takes about an hour to add.

When the decision changes
WhenDecisionWhy
The integration tests cover code that writes refunds to the payment provider and the ledger tableTest mandatory: on every pull request, run a refund test for each provider error response; a second person reviews the testsBlast radius rises to money and Reversibility to costly, because an overpaid refund needs a claw-back
Every query the tests cover filters customer records by tenant IDTest mandatory: an integration test with rows of two tenants for each query, on every pull requestBlast radius rises to safety-or-legal and Detectability to never, because a missing filter shows another customer's records without an error
The failure to catch is a change in a partner's API responses that the partner's sandbox does not showTest it differently: alert in production when the share of failed or empty partner responses changesTest cost rises to heavy, because only the partner's production API shows the change
The service only loads records by ID, and a broken mapping shows an error page that users reportTest minimally: one CI test that starts the app against the database and loads one recordDetectability moves to same-day and Reversibility to trivial, because a lookup by ID stores nothing
The service is an admin tool that only staff use, with no customer data or paymentsTest minimally: one integration test of the main path in CIBlast radius falls to internal, because a failure slows staff and no customer sees it
The app is a side project that only you run on your laptopDo not add integration tests or a CI job; open the pages that read the database after each changeBlast radius falls to none, because you bear every failure alone

What breaks if you don't test

Tests that live on laptops run when a developer remembers them, and the pull request that changes a mapping goes through without them. A nightly run reports the failure a day late, among all of that day's merges. Meanwhile customers see a shorter list or the wrong rows for weeks.

What you lose if you over-test

Tests that call a partner's shared sandbox fail whenever the sandbox is down, so developers rerun every red build, real failures included. An integration job that slows every pull request pushes developers to batch changes into larger ones.

How to test

  1. Run the integration tests in the pull request pipeline as a required status check, so a red run blocks the merge.
  2. Start the production database engine at production's version as a service container or with Testcontainers, and apply the real migrations first.
  3. Replace third-party APIs with a fake HTTP server, and run the few sandbox tests on a schedule.
  4. Give each test its own rows.
  5. Check the CI command: mvn test stops before the Failsafe plugin runs the *IT classes; mvn verify runs them.

When the answer changes

  • The code moves money or filters records by tenant or owner.
  • The failure lives in a partner's API that no sandbox reproduces.
  • Only you use the application.

Real incident + Code example

The nightly run everyone learned to ignore

On a support-ticket product I worked on, 180 integration tests ran nightly against a shared PostgreSQL database. The job failed about once a week because tests left rows behind, so red meant "rerun it". A pull request made the ticket queue page with ORDER BY updated_at LIMIT 25 OFFSET n. A CSV import had given hundreds of tickets the same updated_at, and PostgreSQL gives inconsistent pages when LIMIT and OFFSET lack a unique order: some tickets appeared twice, some never. The integration test for paging failed on most nights after the merge. Twelve days later a support lead found a ticket that had waited eleven days for a reply. The fix ordered by updated_at, id, and the integration tests moved to pull requests, each run on a fresh database container:

# .github/workflows/ci.yml: integration tests on every pull request, as a required check.
on: [pull_request]
jobs:
  integration:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16.4 # the version production runs
        env: { POSTGRES_PASSWORD: test }
        ports: ["5432:5432"]
        options: --health-cmd pg_isready --health-interval 5s --health-retries 10
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: "21" }
      # verify, not test: Failsafe runs the *IT classes only in this phase.
      - run: ./mvnw -B verify
        env:
          SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/postgres
          SPRING_DATASOURCE_USERNAME: postgres
          SPRING_DATASOURCE_PASSWORD: test

FAQ

Should integration tests be included in continuous integration?

Yes, integration tests belong in the CI pipeline of every pull request, and a failure blocks the merge. Tests that depend on a third-party sandbox run on a schedule, so its outages block no merges.

Should integration tests run on every pull request or nightly?

Run integration tests on every pull request. A nightly run reports a failure a day late among all of that day's merges, and a red nightly result that nobody owns soon gets ignored.

How do I run integration tests with a database in CI?

Start the production database engine, at production's version, as a CI service container or with Testcontainers. Apply the real migrations, then run the tests against it.

What if integration tests make CI too slow?

Run integration tests in a job parallel to the unit tests, and split them into shards when that job becomes the slowest step. Share one database container across the run instead of one per test class.