Testing as a Process, Not a Phase
Testing isn't something you do at the end. Learn how SDLC vs STLC thinking, continuous testing, and quality gates in CI/CD change how teams build software.
“We’ll test it when dev is done.”
This sentence has been responsible for more production incidents, more late sprints, and more quality problems than almost any technical decision. It treats testing as a phase — something that happens after development — rather than as a continuous process woven through every stage of building software.
The shift from “testing as a phase” to “testing as a process” is partly technical (CI/CD pipelines, quality gates) but mostly cultural. In this post I’ll explain what that shift looks like in practice and how to implement it step by step.
The Old Model: Testing as a Phase
In the classic waterfall model, the Software Development Lifecycle (SDLC) flows like this:
Requirements → Design → Implementation → Testing → Deployment → Maintenance
Testing occupies one specific phase. Work flows in one direction. Defects found in testing are sent back to implementation — creating a feedback loop that could take weeks.
Even in teams that have adopted Agile, this mental model persists:
- Sprint planning: developers take stories, QA takes stories
- Mid-sprint: developers build, QA waits
- End of sprint: developers “hand off” to QA
- QA finds bugs, developers fix them
- If bugs take too long, features slip to next sprint
Sound familiar? That’s waterfall inside a sprint. The ceremonies changed but the mental model didn’t.
SDLC vs STLC
The key insight is that testing has its own lifecycle — the Software Testing Lifecycle (STLC) — which runs in parallel with development, not after it.
SDLC phases:
- Requirements
- Design
- Implementation
- Testing
- Deployment
- Maintenance
STLC phases:
- Requirements analysis → QA reviews requirements for testability
- Test planning → test strategy, scope, resources
- Test case design → write test cases, define automation scope
- Environment setup → test environments, data, tools
- Test execution → run tests, report defects
- Test closure → metrics, lessons learned, reporting
Here’s the important part: STLC phase 1 starts when SDLC phase 1 starts. QA is reviewing requirements while the product owner is writing them. QA is designing tests while developers are designing the implementation.
In Agile terms: both cycles run within every sprint.
Continuous Testing
Continuous testing means running automated tests at every stage of the delivery pipeline, getting feedback within minutes rather than days.
It’s not just “run CI on every commit.” That’s a start, but continuous testing requires:
- The right tests at the right stage
- Fast enough to not break developer flow
- Actionable results — failures that tell you what broke, not just that something broke
The Continuous Testing Pipeline
Pre-commit → PR/commit → Merge to main → Scheduled
───────────── ────────────── ─────────────── ──────────────
Unit tests Unit tests Integration Full E2E suite
Linting Integration Smoke E2E Performance tests
Type checking Type checking Security scan Security scans
Full integration Accessibility audit
Time: <30s Time: <5min Time: <15min Time: unlimited
The goal: a developer commits code and knows within 5 minutes whether it breaks anything important. They haven’t switched context yet. The fix takes minutes, not hours.
The 10-Minute Rule
Studies on developer productivity consistently show that if a feedback loop takes more than 10 minutes, developers stop waiting for it. They switch to another task, lose context, and the cost of fixing a failure jumps.
Measure your current feedback loop: From git push to “all checks passed” — how long? If it’s over 10 minutes, your pipeline needs optimisation, not just more tests.
Common fixes for slow pipelines:
- Parallelise test execution (run unit tests on multiple agents)
- Separate fast tests (unit) from slow tests (E2E) into different stages
- Cache dependencies (npm, NuGet, pip) between runs
- Run only affected tests on PRs (test impact analysis)
Quality Gates in CI/CD
A quality gate is a hard stop in the pipeline: “do not proceed unless this condition is met.”
Quality gates are the mechanism that makes continuous testing meaningful. Without them, test failures are advisory — easy to ignore under deadline pressure.
Types of Quality Gates
Test pass rate:
# GitHub Actions — fail the job if any test fails
- name: Run unit tests
run: dotnet test --no-build
# Exit code non-zero = tests failed = pipeline stops
Code coverage minimum:
- name: Check coverage
run: |
dotnet test --collect:"XPlat Code Coverage"
dotnet tool run reportgenerator -- \
-reports:coverage.xml -targetdir:coverage-report
# Fail if coverage < 80%
python check_coverage.py --min 80
Note on coverage: 80% coverage is a common threshold, but coverage measures lines executed, not scenarios tested. High coverage with weak assertions is meaningless. Use coverage as a floor, not a ceiling.
Static analysis:
- name: Static analysis
run: dotnet format --verify-no-changes
Security scan:
- name: Dependency vulnerability scan
run: dotnet list package --vulnerable --include-transitive
Performance budget (for frontend):
- name: Lighthouse CI
uses: treosh/lighthouse-ci-action@v10
with:
assertions:
performance: ['error', { minScore: 0.9 }]
first-contentful-paint: ['error', { maxNumericValue: 2000 }]
Branch Protection as Quality Enforcement
GitHub branch protection rules make quality gates mandatory:
# In repository settings (or via API):
# Require status checks to pass before merging:
# - CI / unit-tests
# - CI / integration-tests
# - CodeQL
# Require branches to be up to date before merging
# Require at least 1 approving review
With these in place, no code reaches main without passing the quality gates. No exceptions. No “we’ll fix it in the next PR.”
From Phase to Process — A Practical Transformation
You can’t change a team’s quality culture overnight. But you can change it in five steps, each delivering value on its own:
Step 1 — QA at sprint planning (this sprint) Invite QA to sprint planning. For each story, ask: “How will we know this is done?” Document the answer as acceptance criteria. Cost: 0. Value: immediate.
Step 2 — Pre-commit hooks for unit tests (this week) Add a git pre-commit hook that runs unit tests before allowing a commit:
# .git/hooks/pre-commit
npm test -- --watchAll=false --passWithNoTests
Developers get feedback before they push — not 20 minutes later in CI.
Step 3 — Merge gate: green CI required (this sprint) Enable branch protection rules. No PR merges without passing CI. This alone eliminates a large class of “oops I forgot to run tests” incidents.
Step 4 — Definition of Done includes test criteria (this sprint) Add to your DoD: “Unit tests pass,” “New code has test coverage,” “Exploratory session completed for new features.” Make it visible — post it in Jira, on Confluence, wherever the team works.
Step 5 — Scheduled exploratory sessions (recurring) Block 60–90 minutes per sprint for structured exploratory testing on recently released features. This is when you find the bugs that scripted tests missed.
Measuring the Shift
How do you know if the transformation is working? Track these metrics:
| Metric | Before | Target |
|---|---|---|
| Time from commit to test feedback | 30+ minutes | < 5 minutes |
| Defects found in production | Baseline | Decreasing quarter over quarter |
| Defects found by QA vs found by developers | QA finds most | Developers find 50%+ |
| Sprint velocity lost to late QA findings | High | Near zero |
| Mean time to detect (MTTD) | Days | Hours |
Conclusion
The shift from “testing as a phase” to “testing as a process” is one of the highest-leverage changes a software team can make. It reduces production incidents, shortens sprint cycles, and makes quality a shared responsibility rather than a bottleneck.
It’s also a cultural change, which means it requires patience and consistency. The technical pieces — quality gates, CI/CD pipelines, pre-commit hooks — are the easy part. The hard part is changing the mental model.
Start with Step 1: involve QA in sprint planning this sprint. It costs nothing and changes everything.
This is the fifth post in the Series 1 — QA General series.