Exploratory Testing
What it is
Section titled “What it is”Exploratory testing is simultaneous learning, test design and execution. Instead of following a predefined script, the tester designs and runs tests as understanding grows, following curiosity and risk rather than a checklist. It explores where scripted tests cannot: the unexpected, the ambiguous, the integration seam nobody specified.
charter ──► session (time-boxed) ──► notes / debrief │ │ │ └ a mission └ explore + learn + test └ findings → new tests or bug reportsThe disciplined form is Session-Based Test Management (SBTM): each session is time-boxed (typically 60-120 minutes), driven by a written charter (a one-line mission), and produces a debrief and notes. Heuristics give the exploration structure — for example SFDIPOT (Structure, Function, Data, Interfaces, Platform, Operations, Time) or the quality criteria CRUSSPIC STMPL.
When to use it / when to avoid it
Section titled “When to use it / when to avoid it”Use it for
- New or poorly specified features where scripted tests would miss things
- Investigating a suspicious bug where the cause is unknown
- Complementing automation — humans explore, machines repeat
- Finding the risks that requirement documents never mentioned
Avoid it when
- The check is stable and repeatable → automate it instead
- You need an auditable pass/fail record for a release → use acceptance testing
- You need to prove nothing else broke → use regression testing
Frameworks & tools
Section titled “Frameworks & tools”| Tool | Ecosystem | Notes |
|---|---|---|
| Session sheets / SBTM | Process | Charter, time-box, debrief; the core discipline |
| Rapid Reporter | Multi | Timed-session note-taking tool for explorers |
| Exploratory Testing Chrome Extension | Browser | Tracks actions and captures screenshots during a session |
| Charters / mind maps | Process | Structure areas and risks before exploring |
| Heuristics (SFDIPOT, FEW HICCUPPS) | Process | Mnemonics that make exploration systematic |
| Playwright / Cypress | Multi | Capture a found path as an automated check |
| DevTools | Browser | Inspect network, console and layout live while exploring |
| Bug trackers (Jira, GitHub Issues) | Multi | Turn findings into tracked, reproducible reports |
Quick setup
Section titled “Quick setup”# Chrome extension for session capture, plus a scripted repro after you find a bugnpm install -D @playwright/testnpx playwright codegen https://staging.example.com# Turn a found path into a repeatable check once exploration endspip install pytest playwrightplaywright codegen https://staging.example.com# Rapid Reporter is a standalone tool; use Go for any harness you buildgo test ./exploratory/... -run TestFuzz<!-- Jira/Xray integration lives in your tracker; SBTM sheets are the workflow --><dependency> <groupId>io.github.bonigarcia</groupId> <artifactId>webdrivermanager</artifactId> <version>5.9.2</version> <scope>test</scope></dependency># Session sheets + a tracker is the core; automate repros with Playwright .NETdotnet add package Microsoft.Playwright.NUnitExample
Section titled “Example”Charter: Explore coupon redemption at checkout to find pricing errors.Areas: discount stacking, expired codes, currency rounding, partial refundsDuration: 90 minutesHeuristics: SFDIPOT (Data, Time), FEW HICCUPPS (Consistency, Claims)Learnings: -Bugs: -import { test, expect } from '@playwright/test';
test('explored: stacking two coupons double-applies the discount', async ({ page }) => { await page.goto('/checkout'); await page.getByLabel('Coupon').fill('SAVE10'); await page.getByRole('button', { name: 'Apply' }).click(); await page.getByLabel('Coupon').fill('SAVE10'); await page.getByRole('button', { name: 'Apply' }).click();
await expect(page.getByTestId('order-total')).toHaveText('$90.00');});def test_stacking_coupons_does_not_double_apply(page): page.goto("/checkout") page.get_by_label("Coupon").fill("SAVE10") page.get_by_role("button", name="Apply").click() page.get_by_label("Coupon").fill("SAVE10") page.get_by_role("button", name="Apply").click() assert page.get_by_test_id("order-total").inner_text() == "$90.00"func TestStackingCouponsDoesNotDoubleApply(t *testing.T) { if err := page.Goto("/checkout"); err != nil { t.Fatal(err) } applyCoupon(t, page, "SAVE10") applyCoupon(t, page, "SAVE10") if got := totalText(t, page); got != "$90.00" { t.Fatalf("mong doi $90.00, nhan %s", got) }}@Testvoid stackingCouponsDoesNotDoubleApply() { driver.get(baseUrl + "/checkout"); applyCoupon(driver, "SAVE10"); applyCoupon(driver, "SAVE10"); assertEquals("$90.00", totalText(driver));}[Test]public async Task StackingCouponsDoesNotDoubleApply(){ await Page.GotoAsync("/checkout"); await ApplyCouponAsync("SAVE10"); await ApplyCouponAsync("SAVE10"); Assert.That(await TotalTextAsync(), Is.EqualTo("$90.00"));}Anti-patterns & best practices
Section titled “Anti-patterns & best practices”- Always charter the session. A mission keeps exploration focused and reportable.
- Time-box it. Fixed sessions prevent unbounded wandering and make effort measurable.
- Take notes as you go. Undocumented exploration is lost knowledge; notes become tests and bugs.
- Use heuristics for coverage. SFDIPOT and FEW HICCUPPS turn curiosity into structure.
- Convert findings into automation. Once a bug is understood, capture a repeatable check.
- Debrief every session. Review learnings, bugs and new charters; exploration should compound.
Self-assessment quiz
Section titled “Self-assessment quiz”Self-assessment quiz
Pick one answer per question, then press Check answers.