Building a CI/CD Testing Pipeline Engineers Actually Trust: Quality Gates That Work
A CI/CD pipeline without automated testing is not DevOps. It is a faster way to ship broken software. Here is how to build quality gates that engineers actually trust.
Contents
The 5-Stage Quality Gate Pipeline
Stage 1: Commit Gate (Under 2 Minutes)
- Linting and formatting checks
- Type checking (TypeScript strict mode)
- Unit tests for changed files only
- Gate: All must pass. Zero tolerance.
Stage 2: PR Gate (Under 10 Minutes)
- Full unit test suite
- Integration tests for affected modules
- API contract tests
- Security scanning (SAST)
- Gate: 80% coverage on changed files. Zero critical security findings.
Stage 3: Merge Gate (Under 30 Minutes)
- Full E2E test suite (parallelized)
- Visual regression tests
- Performance budget checks
- Gate: All E2E pass. No visual regressions above threshold. Response times within budget.
Stage 4: Nightly Gate
- Extended E2E across all browsers
- Load testing (k6 or JMeter)
- Security scanning (DAST)
- Accessibility audits
- Gate: Generates release confidence score.
Stage 5: Release Gate
- Release confidence score review (human decision)
- Exploratory testing sign-off
- Stakeholder approval
- Gate: Confidence score above 85%. Zero P0/P1 open bugs.
GitHub Actions Example
name: Quality Gates Pipeline
on:
pull_request:
branches: [main]
jobs:
commit-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lint
- run: npm run type-check
- run: npm run test:unit -- --changed
pr-gate:
needs: commit-gate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run test:unit -- --coverage
- run: npm run test:integration
- run: npm run test:api-contracts
- name: Check coverage threshold
run: |
COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "Coverage $COVERAGE% is below 80% threshold"
exit 1
fi
merge-gate:
needs: pr-gate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npx playwright install --with-deps
- run: npx playwright test --shard=1/4
- uses: actions/upload-artifact@v4
if: failure()
with:
name: test-results
path: test-results/
