Smoke Testing vs Sanity Testing: Differences, When to Use Each
Software teams ship code continuously. Between a commit and a release, a handful of quick checks can stop obvious regressions before they reach users. Smoke testing and sanity testing are two of those
Smoke Testing vs Sanity Testing: Differences, When to Use Each
Software teams ship code continuously. Between a commit and a release, a handful of quick checks can stop obvious regressions before they reach users. Smoke testing and sanity testing are two of those checks, yet they are often confused or used interchangeably. This guide explains what each technique really means, where it belongs in the delivery pipeline, who typically owns it, and how to automate a lightweight but reliable gate that runs on every build.
---
1. What Smoke Testing Really Is
1.1 Definition and Goal
Smoke testing is a shallow, wide‑ranging verification that the most critical paths of an application start up and respond without fatal errors. The term comes from hardware: you power on a device and look for smoke; if none appears, you assume the basic circuitry works. In software, a smoke suite answers the question: “Can the system be used at all?”
A successful smoke run means:
- The application launches (or the service starts).
- Core navigation (home screen, main menu, login) is reachable.
- No uncaught exceptions, crashes, or ANRs (Application Not Responding) appear during the exercised flow.
- Basic integrations (database connectivity, authentication endpoint) return expected responses.
If any of these fail, the build is considered broken and should not proceed to further testing.
1.2 Typical Scope
A smoke suite touches a small percentage of total test cases—often 5‑15 %—but those cases cover:
| Area | Example Checks |
|---|---|
| Application start‑up | Splash screen → main activity loads within 2 s |
| Primary navigation | Tap each bottom‑tab icon; verify target screen appears |
| Authentication | Enter valid credentials; receive token or redirect to dashboard |
| Core data flow | Create a minimal entity (e.g., add a item to cart) and see it persisted |
| External call health | Ping a dependent API; verify HTTP 200 or fallback behavior |
The checks are deliberately independent of deep business rules. They do not validate calculations, edge‑case inputs, or UI polish.
1.3 Who Owns Smoke Tests?
- DevelopersDevelopers** write the initial smoke scripts while building a feature, because they know which calls must succeed for their code to be usable.
- QA engineers maintain the smoke suite as a shared asset, adding new critical paths as the product evolves.
- DevOps / Release engineers trigger the smoke run as the first gate in CI, often blocking promotion to staging if it fails.
---
2. What Sanity Testing Really Is
2.1 Definition and Goal
Sanity testing is a narrow, focused verification that a recent change—usually a bug fix or a small feature—has not broken the surrounding functionality. Unlike smoke, which asks “does the system start?”, sanity asks “did we fix what we intended, and did we not break anything else nearby?”
A sanity pass is performed after a developer has addressed a defect and before the change goes through full regression. It confirms that the specific area under work behaves as expected and that adjacent modules remain stable.
2.2 Typical Scope
Sanity targets the changed component plus its immediate dependencies. The set of checks is usually derived from the ticket description or the commit message. Typical sanity items include:
| Changed Element | Sanity Checks |
|---|---|
| Fixed login validation | Try login with previously invalid email; expect proper error message |
| Updated checkout discount logic | Apply a coupon; verify discount applied and total recalculated |
| Modified push‑notification payload | Send a test notification; confirm payload fields match spec |
| Adjusted accessibility label | Inspect element via accessibility tree; verify label reads correctly |
| Altered database migration script | Run migration on a clone; ensure schema version increments without error |
Because the scope is limited, a sanity suite can often be executed in under two minutes on a single device or container.
2.3 Who Owns Sanity Tests?
- Developers usually write sanity checks themselves, treating them as an extension of unit‑test‑style verification but at the integration level.
- QA may review and add sanity checks for high‑risk areas that developers overlook (e.g., side‑effects on billing when changing a UI flow).
- Release managers sometimes request a sanity sign‑off before allowing a hot‑fix to bypass the full regression cycle.
---
3. Smoke vs Sanity: Side‑by‑Side Comparison
| Aspect | Smoke Testing | Sanity Testing |
|---|---|---|
| Purpose | Verify basic stability and readiness for further testing | Confirm a specific fix/feature works and hasn’t broken nearby code |
| Depth | Broad but shallow (many touchpoints, little detail) | Narrow but deep (few touchpoints, detailed validation) |
| When Run | First gate after every build (CI) or after deployment to a test environment | After a defect fix, before full regression; also during release‑candidate validation |
| Typical Duration | 2‑10 minutes (parallelized across devices) | < 2 minutes (often single‑device) |
| Ownership | Shared QA/DevOps; developers contribute initial scripts | Primarily developers; QA may augment |
| Pass/Fail Criteria | Any crash, ANR, or failure to reach core screens = fail | Failure of the specific sanity check = fail; unrelated minor issues may be tolerated |
| Relation to Other Test Levels | Precedes regression and acceptance; acts as a build‑gate filter | Sits between unit/integration testing and regression; validates change before regression |
| Automation Suitability | Highly suited to automation; benefits from parallel execution | Also automatable, but often kept lightweight; sometimes manual exploratory checks are added |
---
4. Where Smoke and Sanity Live in the CI/CD Pipeline
4.1 Typical Pipeline Stages
- Commit – developer pushes code.
- Unit‑test stage – runs fast, isolated tests.
- Build & package – compiles APK/AAB or bundles web assets.
- Smoke gate – executes the smoke suite on a fresh emulator/device or a containerized browser.
- If smoke passes → proceed to integration / component tests.
- If smoke fails → block pipeline, notify owner, and halt further stages.
- After integration tests → optional sanity window for hot‑fixes or small changes.
- Regression suite (nightly or on demand) – broader validation.
- Acceptance / UAT – business‑oriented validation.
- Production deploy – final release.
4.2 Timing Details
- Smoke runs on every commit (or every scheduled build) because its cost is low and its value high: catching a broken build early saves hours of wasted debugging.
- Sanity runs on demand: after a developer marks a ticket as “Ready for QA” or when a release‑candidate branch is cut. Some teams also run a sanity check as part of a pre‑merge step for branches that only touch high‑risk code (e.g., payment gateway).
- In continuous deployment pipelines that promote directly to staging after a successful smoke, a sanity step may be inserted before the promotion to production, acting as a final sanity check on the release candidate.
4.3 Example CI Configuration (GitHub Actions)
name: CI Pipeline
on:
push:
branches: [ main ]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: '17'
- name: Build Android APK
run: ./gradlew assembleDebug
smoke:
needs: build
runs-on: macos-latest # uses Android emulator
timeout-minutes: 15
steps:
- uses: actions/checkout@v3
- name: Install Node & Appium
run: |
npm install -g appium
appium driver install uiautomator2
- name: Start Emulator
run: |
emulator -avd Pixel_4_API_33 -no-window -no-audio &
# wait for device
adb wait-for-device
adb shell getprop sys.boot_completed
- name: Run Smoke Suite (Appium + Python)
run: |
pip install -r requirements.txt
python -m pytest tests/smoke/ -v --junitxml=smoke-results.xml
- name: Publish Results
if: always()
uses: actions/upload-artifact@v3
with:
name: smoke-report
path: smoke-results.xml
sanity:
# triggered manually via workflow_dispatch or by a label on PR
if: github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'needs-sanity')
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Playwright
run: npm i -D @playwright/test
- name: Run Sanity Checks (Playwright)
run: npx playwright test tests/sanity/ --reporter=json --output=sanity-results.json
- name: Upload Sanity Artifact
if: always()
uses: actions/upload-artifact@v3
with:
name: sanity-report
path: sanity-results.json
*The smoke job runs on every push; the sanity job is optional and can be triggered manually or via a label.*
---
5. Building a Fast, Reliable Smoke Suite
5.1 Selecting the Right Tests
A smoke suite should be stable, fast, and representative of real user entry points. Start by mapping the user journey matrix: list all major entry points (home, login, search, settings) and the core actions reachable from each (view product, add to cart, submit form). For each entry point, pick one verification that confirms the path is alive.
| Entry Point | Core Action | Smoke Check |
|---|---|---|
| Home screen | Open app | Verify main activity appears within 2 s |
| Login | Submit valid credentials | Receive auth token or redirect to dashboard |
| Search | Type a query and press enter | Results list shows at least one item |
| Cart | Add first product | Cart badge increments |
| Checkout | Proceed to payment screen | Payment method list loads |
| Settings | Toggle a switch | Switch state changes and persists |
If a feature is behind a feature flag, the smoke test should respect the flag (i.e., test both on and off states if the flag can be toggled via API).
5.2 Test Data Strategies
- Use static, deterministic data that exists in every test environment (e.g., a pre‑seeded user with known credentials).
- Avoid reliance on data that changes frequently (e.g., live product catalog) unless you can mock or stub the backend.
- For mobile, push a known‑state database onto the device before each smoke run using
adb pushor a test‑only content provider.
5.3 Automation Frameworks
| Platform | Recommended Framework | Reason |
|---|---|---|
| Android native | Appium (Java/Kotlin or Python) + Espresso (for in‑app speed) | Cross‑language, works on real devices and emulators |
| iOS native | Appium + XCUITest | Same rationale |
| Hybrid / React Native | Appium (supports web views) or Detox (if you prefer JS) | Handles native bridges |
| Web | Playwright (JavaScript/TypeScript) or Cypress | Fast, reliable, built‑in auto‑wait, parallel sharding |
| API‑only services | REST‑Assured (Java) or pytest‑requests (Python) | Direct HTTP validation |
5.4 Parallel Execution & Resource Management
- Device farms (Firebase Test Lab, AWS Device Farm, or an in‑house pool) allow you to run the same smoke script on multiple screen sizes, OS versions, and locales simultaneously.
- For web, Playwright’s test sharding (
--shard=1/3) splits the suite across CI containers. - Keep each test under 30 seconds; longer tests defeat the purpose of a quick gate. Use explicit waits only for unavoidable asynchronous events (e.g., network response).
5.5 Flaky‑Test Mitigation
Flaky smoke tests erode trust in the gate. Apply these tactics:
- Deterministic start‑state – wipe app data or reset the emulator before each run.
- Retry wrapper – allow a single retry only for known intermittent issues (e.g., occasional emulator boot delay).
- Log and metrics – capture start‑time, end‑time, device logs, and screenshot on failure; feed them into a dashboard to spot trends.
- Isolate external dependencies – stub or mock third‑party services (payment gateway, analytics) using tools like WireMock or MockServer; only call the real endpoint in a separate “canary” job that runs less frequently.
5.6 Using SUSA for Autonomous Smoke
SUSA can ingest an APK or a web URL and automatically generate a smoke‑like exploration: it launches the app, taps primary navigation elements, attempts login with a set of test credentials, and records any crashes or ANRs. Because SUSA builds its own behavior models (curious, impatient, novice, etc.), it can surface issues that a scripted smoke suite might miss—such as a dialog that blocks the main thread only under a specific locale.
To incorporate SUSA into the pipeline:
# Install the agent
pip install susatest-agent
# Run a smoke exploration against a locally built APK
susatest run \
--app ./build/outputs/apk/debug/app-debug.apk \
--personas curious impatient \
--timeout 180 \
--output susa-smoke-report.json
The JSON report includes a PASS/FAIL flag based on whether any critical flow terminated unexpectedly. You can fail the CI step if the report contains a FAIL.
---
6. Designing Effective Sanity Checks
6.1 Risk‑Based Selection
When a developer fixes a bug, ask:
- What component changed?
- Which integrations does it touch?
- What are the failure modes most likely to regress?
Create a sanity checklist that answers those questions. For a bug in the “apply coupon” function, the checklist might be:
- Load the cart with at least one item.
- Apply a valid coupon code → discount appears, total updates.
- Apply an expired coupon → appropriate error message shows.
- Apply a coupon that exceeds cart total → system blocks and shows warning.
- Navigate away and back → coupon remains applied (state persistence).
6.2 Leveraging Existing Test Cases
Often, a subset of an existing regression test already covers the needed ground. Instead of writing from scratch, tag those regression tests with a label like sanity‑candidate. Your CI can then run only the tagged tests when a sanity trigger fires.
6.3 Example: Mobile Sanity Script (Appium + Python)
import time
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
def test_apply_coupon(driver):
# 1. Ensure we are on the cart page
cart = driver.find_element(AppiumBy.ID, "cart_icon")
cart.click()
assert driver.find_element(AppiumBy.ID, "empty_cart_msg") is None
# 2. Add a product if cart empty
add_btn = driver.find_element(AppiumBy.ID, "add_to_cart_button")
add_btn.click()
time.sleep(1) # allow UI update
# 3. Apply valid coupon
coupon_input = driver.find_element(AppiumBy.ID, "coupon_input")
coupon_input.send_keys("SAVE10")
apply_btn = driver.find_element(AppiumBy.ID, "apply_button")
apply_btn.click()
# 4. Verify discount
discount_text = driver.find_element(AppiumBy.ID, "discount_amount").text
assert "$2.00" in discount_text # assuming $20 item, 10% off
# 5. Verify total
total_text = driver.find_element(AppiumBy.ID, "total_amount").text
assert "$18.00" in total_text
# 6. Apply expired coupon – expect error
coupon_input.clear()
coupon_input.send_keys("OLD20")
apply_btn.click()
error_msg = driver.find_element(AppiumBy.ID, "coupon_error").text
assert "expired" in error_msg.lower()
# Pytest hook to get driver fixture
This script runs in under 15 seconds on a typical emulator and validates the exact behavior changed by the ticket.
6.4 Using SUSA for Targeted Sanity
SUSA’s persona‑based exploration can be constrained to a specific screen or flow via the --start-activity or --start-url flag. For a sanity check on a modified checkout screen, you might run:
susatest run \
--app ./app-debug.apk \
--start-activity com.example.app.CheckoutActivity \
--personas novice power_user \
--max-steps 30 \
--output susa-sanity-checkout.json
The resulting report tells you whether any of the selected personas encountered a crash, ANR, or accessibility violation while navigating the checkout flow. If the report is clean, you have a lightweight sanity signal that complements the developer’s scripted check.
---
7. Automation Strategies and Tooling Details
7.1 Test Execution Orchestration
- Local dev – run smoke/sanity via IDE or a simple script (
./gradlew connectedAndroidTest). - CI – wrap the execution in a Docker image that contains the device emulator, browsers, and all dependencies. This guarantees reproducibility across branches.
- Scheduled runs – in addition to per‑commit smoke, run a full‑device‑matrix smoke nightly to catch OS‑specific regressions that may not appear on the default emulator image.
7.2 Reporting and Metrics
A useful smoke/sanity report includes:
| Metric | Why It Matters |
|---|---|
| Pass/Fail count | Immediate gate decision |
| Execution time per test | Spot slow tests that need optimization |
| Device/OS breakdown | Identify platform‑specific flakiness |
| Screenshot/video on failure | Quick visual debugging |
| Logcat / console output | Root‑cause clues |
| Trend graph (pass rate over time) | Detect degradation early |
Many CI systems (GitHub Actions, GitLab CI, Jenkins) can publish JUnit XML or TestResults.json; plug those into a dashboard like Grafana or Allure for historical view.
7.3 Dependency Management
- Pin versions of Appium, Playwright, and device images in a
requirements.txtorpackage.json. - Use a container image that bundles the Android SDK, emulator system images, and Node.js; tag it with the version of your test framework.
- Cache the emulator snapshots between CI runs to shave off boot time (most CI providers allow saving a directory as a cache).
7.4 Handling Permissions and Dialogs
Mobile apps often present runtime permission dialogs (location, camera, notifications). In a smoke run you want to grant them automatically so they don’t block the flow.
- With Appium:
driver.manage().settings().setSetting(Settings.SETTING_WAIT_FOR_IDLE_TIMEOUT, 0);
driver.resetPermissions(); // grants all declared permissions in manifest
--use-fake-ui-for-media-stream and --disable-features=VizDisplayCompositor to bypass prompts.7.5 Security and Smoke
A smoke suite should not store real credentials. Use environment variables injected at runtime (TEST_USER, TEST_PASS) and never commit them. For OAuth flows, consider using a mock identity provider that returns a predetermined token.
---
8. Edge Cases That Only Appear in Production
Even the most thorough smoke/sanity can miss issues that surface only under real‑world load or specific user conditions. Below are common categories and mitigations.
| Category | Example | Detection Strategy |
|---|---|---|
| Network variability | API latency spikes cause timeouts that are invisible on a fast CI network | Run smoke against a throttled network profile (e.g., netem on Linux or Chrome DevTools throttling) |
| Device fragmentation | Certain low‑end RAM devices kill background services, causing ANRs | Include a low‑spec device profile in the device farm; enable “don’t keep activities” developer option |
| Locale & internationalization | Right‑to‑left languages layout break, causing overlapped UI | Add a smoke run with ar-EG or he-IL locale; verify that UI elements are not clipped |
| Accessibility | TalkBack or VoiceOver reads incorrect labels, leading to failed navigation for assistive‑tech users | Run smoke with accessibility services enabled; check for missing contentDescription or ARIA labels |
| Permission revocation at runtime | User denies location after initially granting; app crashes when trying to access it | Simulate permission revocation via adb shell pm revoke or Playwright’s context.grantPermissions([]) |
| Background/foreground transitions | OS kills app while in background; restart loses state, leading to null‑pointer exceptions | Use adb shell am send-trim-memory or Playwright’s context.waitForEvent('close') to test state restoration |
| Battery optimizations | Aggressive Doze mode stops alarms, causing missed notifications | Disable battery optimization for the test package or test with a charged device and keep‑awake flag |
| Third‑party SDK updates | A new version of an analytics SDK introduces a method that throws on older Android versions | Keep smoke suite aligned with the minimum supported SDK version; run against that version explicitly |
8.1 Simulating Network Conditions
*Android*:
adb shell tc qdisc add dev wlan0 root netem delay 200ms loss 5%
*Playwright*:
context = browser.new_context()
await context.route("**/*", lambda route: route.continue_(headers={}))
await context.set_offline(False)
await context.set_network_conditions(
latency=200,
download_throughput=500*1024, # 500 Kbps
upload_throughput=500*1024,
)
8.2 Automated Accessibility Checks
*Android*: Use the Accessibility Test Framework (ATF) bundled with Espresso, or run adb shell uiautomator runtest AccessibilityTest.jar.
*Web*: Playwright integrates with the axe-core library:
import pytest
from playwright.sync_api import expect
from axe_playwright_python import Axe
def test_home_page_accessibility(page):
page.goto("https://example.com")
axe = Axe(page)
axe.inject()
results = axe.run()
assert len(results["violations"]) == 0, f"Accessibility violations: {results['violations']}"
Add this as a sanity step for any UI‑affecting change.
---
9. Checklist for Smoke and Sanity
9.1 Smoke‑Test Checklist (Run on Every Build)
| # | Item | Owner | Frequency | Pass Criteria |
|---|---|---|---|---|
| 1 | App launches to main screen within threshold | Dev/QA | Every build | No crash, main UI visible |
| 2 | Primary navigation (tabs/drawer) reachable | Dev/QA | Every build | All tabs open correct screen |
| 3 | Login with valid credentials succeeds | Dev/QA | Every build | Auth token or dashboard shown |
| 4 | Core data write (e.g., add item) persists after restart | Dev/QA | Every build | Item appears in list after relaunch |
| 5 | Basic API health endpoint returns 200 | Dev/QA | Every build | HTTP 200, valid JSON payload |
| 6 | No uncaught exceptions or ANRs in logcat | Dev/QA | Every build | Log clean of FATAL/ANR lines |
| 7 | Orientation change does not break layout | Dev/QA | Every build (optional) | UI adapts, no overlap |
| 8 | Push notification registration token obtained (if applicable) | Dev/QA | Every build | Token non‑empty, sent to backend |
9.2 Sanity‑Test Checklist (Run After a Change)
| # | Item | Trigger | Owner | Pass Criteria |
|---|---|---|---|---|
| 1 | Modified screen loads without error | Commit/PR label | Developer | Screen appears, no crash |
| 2 | Expected UI element states (enabled/disabled, text) match spec | Commit/PR label | Developer/QA | Values as defined in ticket |
| 3 | Adjacent flows (e.g., navigation to/from screen) still work | Commit/PR label | Developer | Navigation succeeds, no regression |
| 4 | Data persistence related to change is correct | Commit/PR label | Developer | DB/API reflects intended state |
| 5 | Error handling for invalid input shows proper message | Commit/PR label | Developer/QA | Message matches spec, no crash |
| 6 | Performance impact (frame time, launch time) within budget | Commit/PR label | DevOps | < 16 ms per frame, launch < 2 s |
| 7 | Accessibility labels/roles intact | Commit/PR label | QA | No missing contentDescription/Aria-label |
| 8 | No new permission prompts appear unexpectedly | Commit/PR label | QA | All required permissions already granted or silently handled |
You can turn these checklists into Markdown TODO items in your test repository, enabling quick visual tracking.
---
10. Real‑World Examples
10.1 Smoke Suite for an E‑Commerce Android App
Context: A mid‑size retailer releases new builds twice a day. The app features a home carousel, product catalog, search, cart, and checkout.
Smoke Test Set (implemented with Appium + Java, runs on a Firebase Test Lab matrix of 4 devices):
| Test ID | Steps | Expected Outcome |
|---|---|---|
| S01 | Launch app → wait for MainActivity | Main activity visible, no crash |
| S02 | Swipe carousel → verify third slide appears | Slide 3 image loads, no ANR |
| S03 | Tap search icon → enter “phone” → press enter | Results list shows ≥1 item, progress spinner disappears |
| S04 | Select first product → tap “Add to Cart” | Cart badge increments from 0 to 1 |
| S05 | Open cart → verify product name & price | Product details match the catalog entry |
| S06 | Press back to home → open navigation drawer → tap “Profile” | Profile screen loads, user see default avatar |
| S07 | Simulate network loss (via adb shell cmd wifi set-wifi-disabled) → retry search | App shows offline toast, does not crash |
| S08 | Rotate device to landscape → verify layout adapts | No overlapping views, all controls reachable |
| S09 | Send a push notification via FCM → tap notification | App opens to the screen indicated in payload |
| S10 | Close app via recent‑apps → relaunch from scratch | App starts to main screen, previous cart cleared (as per spec) |
Each test is independent and takes ≤ 8 seconds on average. The entire suite runs in ~1 minute on four devices in parallel, comfortably fitting into a CI gate.
Outcome: In the last quarter, this smoke suite caught three regressions: a missing permission declaration that caused a crash on Android 13, a layout bug that hid the search icon on tablets, and a race condition in the cart‑add endpoint that produced duplicate entries under high latency.
10.2 Sanity Check for a Banking Web App’s Transfer Flow
Context: A hot‑fix was applied to the “Enter amount” field to prevent users from submitting transfers with more than two decimal places.
Sanity Test Set (Playwright + TypeScript, runs against a staging URL):
| Test ID | Steps | Expected Outcome |
|---|---|---|
| T1 | Navigate to /transfer → ensure form loads | Form visible, fields enabled |
| T2 | Enter amount 100.005 → click “Continue” | Inline error: “Amount must have at most two decimal places” |
| T3 | Enter amount 50.00 → click “Continue” → confirm OTP screen | OTP screen appears, amount shown as 50.00 |
| T4 | Change amount to 0.00 → click “Continue” | Error: “Amount must be greater than zero” |
| T5 | Fill valid amount 123.45, choose recipient, submit → verify success page | Success page shows reference number and amount 123.45 |
| T6 | Reload the page → verify form cleared | Amount input empty, recipient dropdown reset |
| T7 | Open dev tools → network tab → ensure request payload contains amount:123.45 (as number, not string) | Backend receives correct numeric value |
| T8 | Run axe accessibility check on the form | No violations related to missing labels or contrast |
| T9 | Simulate slow 3G network → submit valid amount | Submission still succeeds, spinner shown, timeout not triggered |
| T10 | Log out and log back in → navigate to /transfer → verify form default state | Form loads clean, no stale data |
The suite runs in ~12 seconds on a single Chrome instance. It caught a regression where the OTP screen erroneously displayed the raw amount with four decimal places due to a formatting pipe that was not updated after the fix.
---
11. Closing Takeaways
- Smoke testing is a build‑gate: it answers “is the system usable at all?” and must be fast, reliable, and run on every commit.
- Sanity testing is a change‑gate: it answers “did we fix what we intended and not break anything nearby?” and is scoped to the risk introduced by a recent modification.
- Both techniques complement each other and sit below full regression and acceptance testing in the testing pyramid.
- Automation is essential: choose a framework that matches your platform (Appium for mobile, Playwright/WebDriver for web), keep each test under 30 seconds, and run them in parallel on a device/browser matrix.
- Flakiness is the enemy of a smoke gate; mitigate it with deterministic state, limited retries, and rich logging.
- Leverage autonomous tools like SUSA for exploratory smoke or targeted sanity runs—they can surface issues that scripted checks miss, especially around edge
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free