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
- Blast radiususers
- Change frequencyregularly
- Detectabilityeventually
- Reversibilitywith-effort
- Test costmoderate
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 | Decision | Why |
|---|---|---|
| The integration tests cover code that writes refunds to the payment provider and the ledger table | Test mandatory: on every pull request, run a refund test for each provider error response; a second person reviews the tests | Blast 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 ID | Test mandatory: an integration test with rows of two tenants for each query, on every pull request | Blast 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 show | Test it differently: alert in production when the share of failed or empty partner responses changes | Test 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 report | Test minimally: one CI test that starts the app against the database and loads one record | Detectability 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 payments | Test minimally: one integration test of the main path in CI | Blast 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 laptop | Do not add integration tests or a CI job; open the pages that read the database after each change | Blast 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
- Run the integration tests in the pull request pipeline as a required status check, so a red run blocks the merge.
- Start the production database engine at production's version as a service container or with Testcontainers, and apply the real migrations first.
- Replace third-party APIs with a fake HTTP server, and run the few sandbox tests on a schedule.
- Give each test its own rows.
- Check the CI command:
mvn teststops before the Failsafe plugin runs the*ITclasses;mvn verifyruns them.
Procedure and references
- docs.github.com/en/actions/use-cases-and-examples/using-containerized-services/creating-postgresql-service-containers
- docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-status-checks-before-merging
- maven.apache.org/surefire/maven-failsafe-plugin
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
Related questions
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.