Quality Gates in CI: Make Your Tests Block a Bad Merge
Tests that run only when someone remembers put the gate in a person. Run them in four CI tiers that each block the next, keep the commit gate under ten minutes, give every run fresh data, and quarantine flaky tests the same day.
Key takeaways
- A quality gate belongs in the pipeline, not in a person who has to remember to run the tests.
- Run tests in tiers by cost: a commit gate under 10 minutes, a merge gate under 20, a nightly run, and a post-deploy smoke under 5.
- A failing tier must block the next stage; a warning-only suite trains everyone to ignore it.
- Keep failure artifacts such as screenshots, videos and traces even when the job fails.
- Give every test its own tagged data and clean it up in teardown, with a scheduled job for what a killed run left behind.
- On a red build, first decide whether the test or the environment failed; they have different owners.
Contents
FAQ
How long should a CI test gate take?
The commit gate that runs on every push should stay under about ten minutes, or engineers start to bypass it. Slower suites, such as full cross-browser UI runs, belong in a nightly or pre-release tier.
Should CI automatically retry failed tests?
Not as a blanket rule. A test that passes on retry is flaky and should be quarantined with an owner. Retrying everything hides real race conditions in the product.
Where should test passwords and tokens live in CI?
In the CI system's secret store, such as GitLab CI/CD variables or GitHub encrypted secrets, never in files committed to the repository.
Sources
Only you and the owner see your answer and rating. Send private feedback
Enjoyed it? Support fx-blog with a coffee.
Related posts
AI in Testing: Use It Without Trusting It, and Test AI Features
Trusting AI output gives you tests that can't fail and an assistant that invents policy. Review every AI-written test by breaking the feature on purpose, keep agents out of the gate, and test AI features with eval sets run in CI.
Green Tests, Broken Checkout: The Checks Functional Tests Miss
A green functional suite can't see a renamed field, a slow peak, an open security hole or a form a keyboard can't finish. Add a Pact contract, a k6 test against an SLO, a ZAP baseline scan, and axe plus a keyboard pass.
Why Your UI Tests Flake, and the Fix for Web and Mobile
Flaky UI tests teach a team to ignore red. Fix the three usual causes: brittle locators, fixed sleeps and shared data. Use role and test-id locators, web-first assertions, API setup, Accessibility IDs on mobile, and same-day quarantine.
Comments
Oldest firstNo comments yet. Start the conversation.
Part 5 left the ShopLite checkout with five kinds of tests (designed cases, API, UI, contracts and scans), and every one of them runs when someone remembers to run it, on one laptop, against whatever data is lying around. This part moves them into the pipeline so a failing check stops the merge.
The repo's CI guide opens with the line this part rests on: quality gates live in the pipeline, not in a person. A person forgets, or is on holiday the week of the release.
The QA Skills Path, in seven parts:
Four tiers, each blocking the next
Match the cost of a test to the stage that runs it, so fast feedback comes first. Here are the repo's four tiers, with the ShopLite suites from parts 2 to 5 placed in them:
Two rules make this a gate. A failing tier blocks the next, because a warning-only suite trains everyone to ignore it. And the commit gate stays under about ten minutes, or engineers bypass it.
The pipeline, in one file
The repo ships the same tiers for GitLab CI and GitHub Actions; the ideas are identical and only the names differ (
parallel: 4in GitLab isstrategy.matrixin Actions). The UI stage is the one to read closely:Four shards cut the wall-clock time by about four.
when: alwayskeeps part 4's screenshots and traces when the job fails, which is when you need them, and the JUnit report shows failures in the merge request.QA_PASSWORDcomes from the CI secret store, never from a file in the repo.Fresh data on every run
Ask why a suite is flaky and the answer is often data: two tests share one user, and one empties the cart while the other is paying. So every test creates what it needs, with a unique tag, and cleans up even when it fails:
A killed job skips teardown, so a scheduled job deletes tagged data older than a day. Cleaner still is a new environment: the repo's
docker-compose.test.ymlruns Postgres ontmpfs, so the database is gone when the pipeline stops.When the build goes red
The first question on a red build is whether the test failed or the environment did. They look the same and have different owners. The repo's Linux recipe greps the log for each kind:
Its sample report finds 49 new failures in the checkout suite, all one error:
ConnectionRefusedError: payment-service:8080. The suite hadn't changed; the payment service wasn't running. When it is the test, and only sometimes, it goes to quarantine:A quarantined test leaves the gate but keeps running nightly, so you still collect data on it.
Try it
Run your fastest suite with
-n autoand time it: that number is your commit gate.The ShopLite checkout now has a gate that blocks a bad merge without anyone watching. Two questions are left. An AI assistant could have drafted half of these tests, so how would you know which ones can't fail? And ShopLite has just added a shopping assistant that answers customers in its own words. Part 7, AI in Testing: Use It Without Trusting It, and Test AI Features, answers both.
Read this with your AI