Where Should Each Test Run in Your Pipeline?

You push code and wait. Five minutes pass. Ten minutes. The pipeline is still running. Finally, it fails -- but the error is from a unit test that should have run in the first thirty seconds. You just wasted ten minutes of your time and compute resources.

This is the core tension in pipeline design: run too many tests early, and developers wait too long for feedback. Run too few, and critical failures slip through to staging or production. The solution is not to run everything everywhere, but to place each type of test at the stage where it gives the most value for the least cost.

The Principle: Fast and Cheap First

The rule is straightforward: run fast, cheap tests early. Run slow, expensive tests later, and only after the earlier tests pass.

"Cheap" here means low in compute time, environment setup, and data preparation. A unit test that runs in milliseconds and needs no database is cheap. An end-to-end test that spins up a full environment, seeds data, and simulates a user journey is expensive.

"Fast" means the feedback loop. A developer should know within a minute or two if their change broke something fundamental. Waiting thirty minutes to discover a basic logic error is not acceptable.

This is not about policy. It is about throughput. The faster developers get feedback, the faster they fix problems. The more expensive a test is to run, the fewer times it should run.

Stage by Stage: Where Tests Belong

Commit Stage: Unit Tests and Contract Tests

The following flowchart summarizes the recommended test placement across the pipeline stages:

flowchart TD A[Commit Stage] --> B[Build Stage] B --> C[Staging Stage] C --> D[Production Stage] A --> A1[Unit Tests] A --> A2[Contract Tests] B --> B1[Integration Tests] B --> B2[Contract Tests] C --> C1[End-to-End Tests] C --> C2[Smoke Tests] D --> D1[Smoke Tests] D --> D2[Synthetic Transactions]

When a developer pushes code, the first thing to run is unit tests. These tests are fast, need no external dependencies, and verify that individual behaviors work correctly. A good unit test proves that a meaningful behavior -- from the perspective of the caller or user -- produces the expected result. It does not care about internal implementation details.

Some teams also run contract tests at this stage. If the change touches an interface or API contract, verifying it early prevents downstream surprises. Contract tests are usually fast enough to run alongside unit tests.

What should not run here: integration tests, end-to-end tests, or any test that requires a database, external service, or full environment. Those belong later.

Build Stage: Integration Tests and Contract Tests Again

After the code compiles and produces an artifact or container image, integration tests enter the picture. At this stage, the application exists as a runnable unit. Integration tests check that the application connects correctly to its dependencies -- databases, message queues, or other services.

These tests typically use test doubles or lightweight containers, not full staging environments. They verify that the wiring is correct: the application can open a connection, send a query, and handle a response.

Contract tests often run again here, against the built artifact. This ensures that the compiled code still satisfies the contract, not just the source code.

Staging Stage: End-to-End Tests and Smoke Tests

Staging should mirror production as closely as possible. This is where you run end-to-end tests -- but only for critical user journeys. Not every page, not every feature. Just the flows that matter most: login, checkout, search, or whatever your core business logic is.

End-to-end tests are slow and expensive. Running them for every minor change wastes time. Reserve them for changes that touch critical paths or introduce new functionality.

Smoke tests also run here. A smoke test is a quick check that the application responds correctly after deployment. If the smoke test fails in staging, do not proceed to production. Stop the pipeline and investigate.

Production Stage: Smoke Tests and Synthetic Transactions

In production, tests must be minimal and must not affect users. Run a smoke test immediately after deployment to confirm that the main endpoints respond with the correct status codes. This is not a deep validation -- just a sanity check that the deployment did not break the application.

Synthetic transactions run periodically, not right after every deployment. They simulate user interactions -- like logging in or completing a purchase -- to detect regressions that staging did not catch. Because they run on a schedule, they catch issues that appear over time, such as memory leaks or data corruption.

What Not to Do

Do not run all tests at all stages. This is the most common mistake. Running end-to-end tests at the commit stage makes no sense -- the environment is not ready, and the feedback is too slow. Running unit tests again at the production stage is wasteful -- the logic did not change between staging and production.

A good rule of thumb: if a test passed at an earlier stage, do not repeat it at a later stage unless the environment change could affect the result. Unit tests do not depend on the environment, so they do not need to run again. Smoke tests do depend on the deployment, so they must run at each new environment.

Risk-Based Testing in the Pipeline

The placement of tests also depends on risk. A change to the payment system or authentication logic warrants more layers of testing. A change to a button color or a typo in a label needs only unit tests and a smoke test.

This is risk-based testing applied to the pipeline: the higher the risk of the change, the more test layers it must pass. The lower the risk, the faster it can move through the pipeline.

Some teams implement this by tagging changes or using branch policies. Critical paths require end-to-end tests in staging. Non-critical paths skip them. This keeps the pipeline fast for low-risk changes while maintaining safety for high-risk ones.

Practical Checklist for Test Placement

  • Unit tests run at commit stage. Fast, no dependencies, immediate feedback.
  • Contract tests run at commit and build stages. Verify interfaces early and against the artifact.
  • Integration tests run at build stage. Check connections to dependencies using containers or test doubles.
  • End-to-end tests run only at staging stage. Cover critical user journeys, not every feature.
  • Smoke tests run at staging and production stages. Quick sanity checks after each deployment.
  • Synthetic transactions run periodically in production. Catch regressions over time.
  • Do not repeat tests across stages unless the environment change matters.
  • Adjust test layers based on risk. High-risk changes get more coverage.

The Takeaway

A well-placed test gives you fast feedback when you need it and thorough validation when you can afford it. The goal is not to test everything everywhere. The goal is to catch the right failure at the right time, so your team can fix it before it reaches production.