Blog
Smoke Testing vs Sanity Testing: A Practical QA Guide
Smoke testing vs sanity testing explained for working QA teams—scope, timing, CI/CD gating, example suites, and how both relate to regression.
- smoke-testing
- test-strategy
- qa
- automation
Smoke testing vs sanity testing matters most when you decide whether a fresh build deserves a full test cycle, or whether a hotfix can go out without re-running everything. Then the distinction becomes a scheduling decision with real cost.
Smoke testing asks is this build worth testing at all? Sanity testing asks did the specific thing we just changed actually work, and did it obviously break its neighbours? Both are shallow by design, and neither replaces regression.
This guide covers scope, timing, CI/CD placement, example suites, and common failure modes. For case-writing craft, start with how to write QA test cases; for suite selection, pair this with regression test cases.
What smoke testing actually verifies
A smoke test is a build-acceptance check. Its job is to prove the deployed artifact is coherent enough to justify deeper testing. The name comes from hardware: power it on, see if smoke comes out.
Practically, a smoke suite confirms that:
- The application starts and serves its entry points
- Critical dependencies (database, cache, auth provider, primary third-party API) are reachable
- One representative end-to-end path completes — usually sign-in plus one core action
- The deployment produced no obvious configuration failure, such as a missing variable or unmigrated schema
Smoke tests are broad and shallow. They touch many subsystems once and assert coarse outcomes. A smoke test should not care whether a validation message is worded correctly; it cares whether the page renders and the request returns something other than a 500.
The design constraint that matters most is runtime. A suite that takes 40 minutes is no longer a smoke suite; it is a slow gate people learn to bypass.
What sanity testing actually verifies
A sanity test is a targeted, post-change confidence check. Something specific changed — a bug fix, a config value, a library upgrade — and you want narrow verification that it landed and behaves, without committing to a full regression cycle.
Sanity testing is narrow and slightly deeper. It concentrates on one area, checks the changed behaviour properly, then samples the immediate blast radius around it.
Concretely, after a fix to password reset token expiry, a sanity pass looks like:
- Request a reset link and confirm it works within the valid window
- Confirm an expired token is now rejected with the corrected message
- Confirm normal sign-in still works, since auth shares the same session layer
- Confirm no second reset email is triggered by the fix's retry logic
That is four checks against one area, not one check against ten areas. Notice step three: sanity testing without any neighbour sampling is just "the developer said it works."
Sanity tests are also often lightly scripted — one of the few legitimate places where a tester works from a short charter rather than a formal case ID, though anything worth repeating should graduate into a written case.
Smoke testing vs sanity testing: comparison table
| Dimension | Smoke testing | Sanity testing |
|---|---|---|
| Core question | Is this build testable? | Did this specific change work? |
| Shape | Broad, shallow | Narrow, moderately deep |
| Trigger | Every new build or deployment | After a specific fix, config change, or patch |
| Scope source | Fixed list of critical journeys | The change itself plus adjacent behaviour |
| Typical runtime | Under 10 minutes | Minutes to under an hour |
| Automation fit | High — stable, repeatable | Mixed — often manual or semi-scripted |
| Owner | Usually CI, unattended | Usually a named tester or the fixing engineer |
| Failure meaning | Stop; the build is rejected | Investigate that area; the build may be salvageable |
| Documented as | A standing suite with IDs | A charter, or a subset of existing case IDs |
The row that resolves most disputes is "scope source." A smoke suite is a fixed list agreed in advance and rarely changed. A sanity scope is derived fresh from what changed. If someone hands you a "standing sanity suite" that never varies, they have built a second smoke suite under a different name.
When each one applies in CI/CD
On every commit to the main branch
Run smoke only. Developers need a fast signal, and any gate longer than their attention span gets worked around with merge overrides. Keep it at API level where possible; API smoke is far more stable than browser smoke.
On a deploy to shared staging
Run smoke immediately after the deployment completes, against the deployed environment rather than a local server. This is where configuration failures surface: the code was fine, the environment variable was not. A pass here unblocks manual testers for the day.
After a bug fix branch is deployed
Run sanity. Testing everything is a waste; testing only the reported reproduction is too narrow. Derive a short list from the change: the fixed behaviour, its inverse, and one or two shared components it touches.
Before promoting a release candidate
Run smoke as an entry gate, then the regression core. Use sanity only for late-arriving fixes inside the release window — it is not a substitute for regression at a release boundary, no matter how tight the schedule is.
After a production hotfix
Run sanity in production against the fix, plus your smallest read-only smoke subset. Decide in advance which checks are production-safe; a hotfix window is the worst time to discover that smoke creates test orders in live payments.
Example smoke suite (illustrative B2B SaaS)
Ten checks, all coarse, all fast, no cosmetic assertions:
| ID | Check | Layer | Why it earns a slot |
|---|---|---|---|
| SMK-01 | Health endpoint returns healthy with database reachable | API | Catches unmigrated or unreachable DB |
| SMK-02 | Sign-in with a seeded user returns a session | API | Auth gateway; nothing else matters if this fails |
| SMK-03 | Dashboard renders for a signed-in user without console errors | UI | Proves the frontend bundle deployed correctly |
| SMK-04 | Create a primary entity (project) via API | API | Primary write path |
| SMK-05 | Created entity appears in its list view | UI | Read path plus indexing wiring |
| SMK-06 | Billing page loads current plan | UI | Third-party billing integration reachable |
| SMK-07 | Background job queue accepts an enqueue | API | Async infrastructure alive |
| SMK-08 | Email provider accepts a send in sandbox mode | API | Notification dependency |
| SMK-09 | Unauthenticated request to a protected route returns 401 | API | Cheap authz guard |
| SMK-10 | Sign-out invalidates the session | API | Session hygiene |
That suite runs in a few minutes and covers most "the build is broken" mornings. It deliberately contains no field-level validation, no edge cases, and no permission matrix — those belong in the deeper packs described in regression test cases and edge case testing.
Example sanity scope for a real change
Change: a developer fixed a bug where discount codes were applied before tax instead of after, in a checkout service.
| Step | Check | Rationale |
|---|---|---|
| 1 | Order with a percentage discount shows the corrected total | The fix itself |
| 2 | Order with a fixed-amount discount shows the corrected total | Same code path, different branch |
| 3 | Order with no discount is unchanged from before | Guards against a regression in the common case |
| 4 | Invoice PDF total matches the order total | Downstream consumer of the same calculation |
| 5 | An existing paid order's historical total is unchanged | Confirms the fix was not applied retroactively |
Five checks, one area, about twenty minutes. Step five is the check that separates sanity testing from a demo.
How smoke and sanity relate to regression
Think of the four activities as scope versus depth:
| Activity | Scope | Depth | Cadence |
|---|---|---|---|
| Smoke | Wide | Very shallow | Every build |
| Sanity | Narrow | Medium | Per change |
| Regression core | Medium-wide | Medium | Every release candidate |
| Regression extended | Wide | Deep | Nightly or pre-release |
Smoke is in practice a subset of regression — its fastest, most critical slice. Sanity is a subset of nothing; it is derived per change, which is why it resists being frozen into a standing suite.
When a sanity check catches something twice for the same feature, promote it into the regression core with an ID and an owner. That is how sanity work compounds instead of evaporating. Selection discipline for that promotion is covered in regression test cases; the field layout for the promoted case belongs in your shared QA test case template.
Step-by-step: building a smoke suite that survives contact with a deadline
1. List your top five business-critical journeys
Not ten, not twenty. Sign-in, the primary create action, the primary read action, the money path, and the exit. Write them as sentences a product manager would recognise.
2. Pick the cheapest possible proof for each
Usually an API call. Reserve UI checks for what only a browser can confirm — that the bundle loaded, the shell renders, a critical page is not blank.
3. Set a hard runtime budget first
Pick a number your team will actually wait for and treat it as a constraint. Every future addition must fit inside it or displace something else.
4. Make failures unambiguous
Each check needs a falsifiable expected result and an owner. "Dashboard looks right" is unusable at 7 a.m. when CI is red; "dashboard returns 200 and renders the account name element" is actionable.
5. Seed data deterministically
Smoke suites break most often on data assumptions. Use a fixture account created by setup code, never a hand-made account someone might delete in a cleanup sprint.
6. Gate on it, then defend the gate
A suite that can be skipped with a checkbox will be skipped. Wire it in as a blocking deploy step and require a logged override with a named approver. Then review it quarterly: if an escape was catchable inside the budget, add that check and drop your least valuable one.
Common mistakes
- Growing smoke into mini-regression. Someone adds "just one more" check every sprint. A year later the suite runs for half an hour and nobody trusts its failures. Enforce the budget.
- Calling a full regression pass "sanity testing" to make it sound cheaper. This distorts every estimate afterwards.
- Sanity testing only the exact reproduction steps from the bug ticket. The reported path was one symptom. Test the branch you fixed and the neighbours it shares code with.
- Cosmetic assertions in smoke. Asserting exact copy or pixel layout fails the gate on harmless changes, which trains everyone to re-run rather than read.
- Treating a smoke pass as release approval. Passing smoke means "keep testing," not "ship it."
- Skipping sanity because the change was "one line." One-line changes to shared calculation or auth code have the widest blast radius in the codebase.
Best practices
- Version the smoke suite as code, in the application repository, reviewed in the same pull requests.
- Prefer API smoke over UI smoke for anything that does not require a browser to prove.
- Keep a documented production-safe subset so hotfix windows do not require improvisation.
- Write sanity scope in the ticket before executing, as three to six bullets. It creates a record of what was and was not checked.
- Automate smoke first, sanity later. Sanity varies per change and rarely repays automation until a pattern repeats.
- Fail loudly and specifically. Include the failing check ID, environment, and deploy SHA in the notification.
For coverage depth decisions upstream of this, see Basic, Standard, or Deep coverage. If your smoke suite leans on API checks, the assertion patterns in API test cases apply directly. When reporting either level, name the limits: "Sanity passed on the discount calculation; full checkout regression has not run yet" is an honest status.
Final checklist
- Smoke suite is a fixed, documented list with stable IDs
- Smoke runtime budget is agreed and enforced
- Smoke covers sign-in, one write, one read, the money path, and session exit
- Smoke assertions are coarse, with no cosmetic checks
- Smoke has a named owner and blocks the deploy pipeline
- A production-safe smoke subset is documented for hotfixes
- Sanity scope is written in the ticket before execution
- Sanity includes the fixed branch, its inverse, and adjacent behaviour
- Repeated sanity checks get promoted into regression with IDs
- Reports state clearly which level of testing has and has not run
Summary
Smoke testing is a fixed, fast gate answering whether a build is worth testing. Sanity testing is a change-derived, narrow check answering whether a fix works and did not obviously damage its surroundings. Smoke belongs in automation on every build; sanity belongs to a tester who knows what changed. Regression sits above both. Keep smoke small enough to stay trusted, and promote recurring sanity checks into owned regression cases.
FAQ
Is sanity testing just a subset of regression testing?
No. Regression cases are pre-written and re-run to protect known behaviour. Sanity scope is derived from a specific change and may never be run again in that exact form. The overlap is that a useful sanity check often deserves promotion into regression afterwards.
Can one suite serve as both smoke and sanity?
Not well. A fixed suite cannot adapt to what changed, which is the point of sanity testing. Teams that try usually end up with a suite too slow to gate builds and too generic to verify fixes.
Should smoke tests run against production?
A read-only subset, yes — after deployments and during hotfix windows. Document exactly which checks are production-safe. Never let a suite that creates orders, sends real emails, or mutates customer records run there by default.
Who should perform sanity testing — QA or the developer who made the fix?
Either can, but the scope should be reviewed by someone who did not write the fix. Authors reliably under-scope their own blast radius — not from carelessness, but because they test the mental model they already had.
What if smoke passes but the build is obviously broken for testers?
That is a suite design gap and the most valuable feedback you can get. Find the smallest check that would have caught it, add it, and remove your least useful existing check to stay inside budget.
CTA — turn recurring sanity checks into owned cases
Sanity work generates more throwaway knowledge than any other QA activity. Use QA Workflow Assistant to convert the checks you keep repeating after fixes into structured, reviewable cases, then decide deliberately which graduate into the smoke or regression packs. Plan limits are on Pricing; pipeline setup lives in Docs.
Related articles
Regression Test Cases: A Practical Guide
A practical guide to regression test cases—how to select, maintain, prioritize, and automate a suite that protects critical journeys without freezing delivery.
August 10, 2026 · 12 min read
Requirements Traceability Matrix (RTM): A Practical QA Guide
A working guide to the requirements traceability matrix—columns that matter, a filled example, linking stories to acceptance criteria to cases to results, and how RTMs fail.
August 15, 2026 · 13 min read
Test Case Priority: How QA Teams Rank What to Run First
A practical guide to test case priority—P0 to P3 schemes, risk-based ranking, worked examples for auth and billing, and how priority drives regression selection.
August 14, 2026 · 14 min read