20 min read · 9 self-checks · Updated June 2026

Black Box · Specification-Based

State Transition Testing

Model a system as a finite set of states and the events that move it between them. Test every valid transition — and verify that invalid transitions are rejected.

Junior Senior ISTQB CTFL v4.0 — 4.2.4

1 The Hook

A NZ courier company builds a parcel-tracking system. A parcel moves through states: Accepted, In Transit, Out for Delivery, Delivered. The team tests the obvious forward path and it all works — scan it in, move it along, mark it delivered. Customers are happy.

Then odd things start happening. A driver accidentally scans a parcel as "Out for Delivery" a second time after it was already marked Delivered, and the system cheerfully sends the customer a fresh "your parcel is on its way" text for a parcel sitting on their porch. Worse, a returned parcel gets scanned back to "In Transit" from "Delivered", and the proof-of-delivery record is silently overwritten. Nobody tested those moves because nobody asked "what happens if this event arrives when the parcel is in that state?"

The forward happy path is the easy 20%. The bugs were all in the transitions that should have been impossible — the events arriving in the wrong state. A system that only guards the steps it expects, and quietly accepts the ones it doesn't, ends up corrupting its own data. The only way to find these is to model every state and ask, for each event, whether it is allowed.

💡
Key Takeaway

State transition testing maps every state a system can be in, every event that moves it between states, and every combination that should be flat-out rejected — use it any time a feature has a named lifecycle (orders, accounts, claims, bookings). Aim for all-transitions (1-switch) coverage as your baseline: one test per valid transition, plus explicit tests for the invalid ones. The mistake most testers make is testing only the happy-path transitions and calling it done; the worst production bugs in state-based systems are invalid transitions the system silently accepts instead of rejecting.

💬
Senior Engineer Insight

Everyone tests the valid transitions. The discipline is testing what cannot happen. In fifteen years across banking, insurance, and government systems in NZ I have seen the same bug pattern repeatedly: a state machine that rigorously enforces the forward path and silently accepts whatever arrives at a terminal state. An CoverNZ claim marked Closed that processes a payment event without complaint. A KiwiSaver withdrawal in Paid Out that accepts a resubmit and overwrites the payout record. The textbook says "test invalid transitions" — what it does not say is that the system never crashes. It just quietly corrupts data. No exception, no log entry, nothing. You find it three months later in a reconciliation report. Test your terminal states first, with every defined event, before anything else.

2 The Rule

Model the system as a fixed set of states and the events that move it between them, then test every valid transition at least once — and just as deliberately, test that invalid transitions are rejected rather than silently accepted.

3 The Analogy

Analogy

The traffic lights at a NZ intersection.

A set of traffic lights has a small number of states — red, green, amber — and strict rules about which can follow which. Green goes to amber, amber goes to red, red goes to green. What must never happen is red jumping straight to green with no amber, or two directions both showing green at once. The whole safety of the intersection rests on the impossible transitions staying impossible.

State transition testing is checking the lights. You confirm every allowed change works — and then you spend real effort trying to force the forbidden ones, because a light that will skip amber when poked is the one that causes the crash. Testing only "green then amber then red" in order misses the dangerous case entirely.

What it is

Many systems can only be in one state at a time, and move between states in response to events. A bank account is either Open, Frozen, or Closed. A booking is Pending, Confirmed, Cancelled, or Completed. State transition testing ensures all these paths work correctly.

The model has four elements:

  • States — the distinct conditions the system can be in.
  • Events (triggers) — inputs or actions that cause a transition.
  • Transitions — the move from one state to another.
  • Actions — what the system does when a transition occurs.

Building the state model

Start with a state diagram (boxes = states, arrows = transitions labelled with event/action). Then convert it to a transition table for easier test case derivation.

Worked example

Online order lifecycle: an order starts as Pending, can be Confirmed, Dispatched, Delivered, or Cancelled.

Order state transition table
Current StateEventNext StateAction
PendingPayment receivedConfirmedSend confirmation email
PendingCustomer cancelsCancelledSend cancellation notice
ConfirmedStock allocated & shippedDispatchedSend tracking number
ConfirmedCustomer cancelsCancelledInitiate refund
DispatchedDelivery confirmedDeliveredClose order, request review
DeliveredCustomer cancelsInvalidError: cannot cancel delivered order
CancelledPayment receivedInvalidError: order is cancelled

Coverage criteria

There are three main coverage levels:

  • 0-switch (all states) — visit every state at least once. Weakest.
  • 1-switch (all transitions) — exercise every valid transition at least once. ISTQB Foundation standard.
  • 2-switch (all transition pairs) — every consecutive pair of transitions. Strong but expensive.

Testing invalid transitions

A complete test suite also tests transitions that should not be possible. Try to cancel a delivered order. Try to dispatch a cancelled order. These tests verify the system rejects invalid state changes gracefully — not silently, and not by corrupting data.

Real-world value: state transition bugs are nasty to find manually. A user who somehow gets an order into an impossible state can create support nightmares. Model it formally and test the invalid paths explicitly.

ISTQB mapping

ISTQB CTFL v4.0 reference
RefTopicLevel
4.2.4State Transition TestingCTFL Foundation
FL-4.2.4 K3Apply state transition testing to derive test casesFoundation LO
FL-4.2.4 K3Achieve specified coverage levelsFoundation LO

NZ example — RealMe identity verification

RealMe is New Zealand’s government identity verification service (used by Revenue NZ, TransitNZ, Benefits NZ). A RealMe account moves through states during verification.

RealMe account — state transition table
Current StateEventNext StateNotes
UnverifiedSubmit documentsPendingDocuments under review
PendingDocuments acceptedVerifiedIdentity confirmed
PendingDocuments rejectedUnverifiedResubmit required
VerifiedFraud flag raisedSuspendedSuspicious activity detected
SuspendedFraud clearedVerifiedAccount reinstated
VerifiedClosure requestedClosedAccount closure
UnverifiedJump to VerifiedInvalidNo documents submitted — must be impossible
SuspendedSelf-close accountInvalidAdmin-only action; user cannot self-close while suspended

All-transitions coverage requires a test case for each valid transition. The two invalid transitions must also be tested — verify the system rejects them gracefully, not silently.

Try it yourself

NZ library book loan system — state transition table

A library book can be in one of four states: Available, On Loan, Reserved, or Overdue. There are exactly 6 valid transitions. For each row, select the From state and To state, and type the triggering event.

# From state Event (what triggers the change?) To state
All 6 valid transitions:
#From stateEventTo state
1AvailableMember borrows bookOn Loan
2On LoanMember returns bookAvailable
3OverdueMember returns bookAvailable
4On LoanDue date passes / book not returnedOverdue
5AvailableMember reserves bookReserved
6ReservedMember borrows / reservation fulfilledOn Loan

4 Industry Reality

🏭 What you actually encounter on the job
  • State diagrams rarely exist. You inherit a system with no documentation. Senior testers reverse-engineer the model from the code, support tickets, and by sitting with a developer for 30 minutes — the diagram comes after the investigation, not before.
  • Requirements use inconsistent state names. One screen calls it "Processed", the API returns "processed", the database column stores "PROC", and Jira calls it "Done". Part of your job is unifying the vocabulary before you can even draw the model.
  • Invalid transitions are often the last thing tested — if at all. Under time pressure, teams ship happy-path coverage and defer the "what if" cases. In practice, the first production incident after go-live is usually an invalid transition that was never checked.
  • Legacy systems have undocumented escape hatches. Admin backdoors, database patches, and manual SQL updates bypass the application's state machine entirely. Real testers ask "is there any other way to change this record?" and include those paths in scope.
  • NZ compliance systems demand explicit invalid-transition evidence. For regulated workflows (Revenue NZ tax returns, CoverNZ claims, RealMe identity) auditors want to see test evidence that specific invalid transitions were attempted and rejected — not just that the happy path passed. Build that into your test report template.

Senior engineer insight

The scenario that changed how I work: I was testing an Revenue NZ income tax filing portal and every valid transition passed cleanly. We shipped. Three weeks later, a batch reconciliation job revealed that some returns marked Filed had quietly re-entered Draft status when a downstream retry hit them overnight — because the system had no guard on that event arriving at a Filed record. Nobody had tested it because the UI had no "send back to Draft" button. My rule ever since: draw a column in your transition table called "events that must be impossible here" and test every cell in it, not just the ones with defined transitions.

The most common mistake I see from graduates is treating a passing 1-switch test suite as complete — they exercise every valid transition and call it done, never touching the rows that should be blank. Those blank rows are where production incidents live.

From the field

We were testing an CoverNZ claims portal for a government integrator — mid-sprint, tight deadline, team confident because every valid transition in the table was green. Then a tester on the payments side noticed that a claim sitting in Closed state was silently accepting a "reassess" event sent by a downstream batch job — no error, no log entry, status quietly flipped back to Under Assessment, and the original closure record was overwritten. The batch job had been retrying stale messages for weeks in production before anyone noticed in a reconciliation report. We added a terminal-state sweep to our definition of done after that: every defined event against every terminal state, bypass the UI, call the API directly, assert a 4xx rejection. It takes 20 minutes and has caught the same class of bug on every project since.

5 When to Use It — and When Not To

⚡ Decision guide

✓ Use it when

  • The system has a clear lifecycle with named states (orders, bookings, applications, accounts, claims).
  • Incorrect transitions would cause data corruption, financial errors, or compliance failures.
  • You need to demonstrate coverage to an auditor or certification body (ISTQB FL-4.2.4 maps directly).
  • Developers used a state machine library or explicit status columns — the model is already implicit in the code.
  • You are testing integrations where two systems exchange status events and disagreeing on valid states causes silent data divergence.

✗ Skip it when

  • The system has no meaningful states — a stateless API endpoint or a simple calculation tool doesn't benefit from a state model.
  • There are only two states (on/off, true/false) — a checklist or boundary test covers it faster with less overhead.
  • The number of states is effectively infinite (e.g. a free-text field with no constrained values) — combinatorial techniques like pairwise testing are a better fit.
  • You have very limited time and the system already has passing unit tests for state logic — add a risk-note and move to exploratory testing instead.
  • The "states" are actually UI screens with no persisted status — use use-case testing or exploratory charters instead.

Context guide

How the right level of State Transition Testing effort changes based on project context.

Context Priority Why
Regulated system (Revenue NZ tax, CoverNZ claims, Benefits NZ benefits, KiwiSaver) Essential Auditors require explicit evidence that invalid transitions were attempted and rejected; financial or legal consequences make any silent state corruption unacceptable.
Government portal or AoG/NZISM-compliant system (RealMe, TransitNZ, visa, permit workflows) Essential Public-facing approval and entitlement workflows (permit approvals, identity verification) carry legal weight; incorrect state changes can produce fraudulent or unlawful outcomes.
Enterprise platform with complex lifecycles (insurance claim, order fulfilment, subscription billing) High Multi-team systems often have state events fired by batch jobs and APIs that bypass the UI; the only defence against silent corruption is server-side state enforcement tested end-to-end.
Agile sprint work on a feature with a status field (user account, booking, support ticket) Medium Draw a lightweight transition table in planning, cover 1-switch within the sprint, and add invalid-transition checks for any terminal state; skip 2-switch unless the feature is high-risk.
Legacy migration (re-platforming an existing NZ claims or ERP system) Medium Start by querying SELECT DISTINCT status against real data — legacy systems often have undocumented ghost states from old migrations; build the model from the data, not the spec, then verify the new platform handles each one.
Small startup MVP or stateless utility (calculator, search filter, read-only report) Low If there is no status column in a database there is probably no state machine to model; apply equivalence partitioning or exploratory testing first, and revisit when a lifecycle emerges.

Trade-offs

What you gain and what you give up when you choose State Transition Testing.

Advantage Disadvantage Use instead when…
Forces you to enumerate every state/event combination before writing a single test — gaps in the table reveal missing requirements immediately. Upfront modelling cost is high. Building an accurate transition table for a complex lifecycle (8+ states) takes hours, and the table becomes stale as the feature evolves. The feature changes every sprint and maintaining the table would cost more than the bugs it finds — use exploratory testing with a checklist of known-risky transitions instead.
Makes invalid transitions first-class test citizens — you get explicit evidence that forbidden state changes were attempted and rejected, exactly what NZ government auditors ask for. Invalid-transition tests are brittle: when requirements add a new valid path, an existing "invalid" test must be updated or it produces a false failure. The business rules change too frequently for stable forbidden-transition definitions — use property-based testing to assert invariants (e.g. balance never goes negative) instead of enumerating forbidden transitions.
Maps directly to ISTQB FL-4.2.4 and produces traceable test coverage metrics (0/1/2-switch) that satisfy certification bodies and compliance frameworks with minimal argument. Coverage metrics can give false confidence — 1-switch says you exercised each transition once, but says nothing about concurrent transitions, race conditions, or the data values carried through each one. Concurrency and race conditions are the primary risk — use stress testing or model-checking tools (e.g. TLA+) rather than manual state transition test cases.
Scales well for integration testing between systems — if two services share a status field (e.g. TransitNZ and a provider portal both update permit status), the table surfaces exactly which transitions each party may and may not trigger. Poor fit for systems where "state" is computed dynamically from many fields rather than stored in a single status column — the model becomes misleadingly simple. The system's effective state is a function of five or more fields with no canonical status value — use decision table testing or equivalence partitioning across those fields instead.
Provides a shared vocabulary for the whole team — developers, testers, BAs, and support staff all reason from the same state names, reducing "works on my machine" debates about what the system should do in edge cases. The technique only covers the cases you thought to model. Hidden states (database values that should not exist but do, due to legacy migrations or manual SQL patches) are invisible to the table. You suspect undocumented states or admin backdoors exist — start with exploratory testing or a database query for unexpected status values before building a formal model.

6 Best Practices

✓ What experienced testers do
  • Build the transition table before writing test cases. The table forces you to enumerate every state/event combination and immediately reveals gaps — rows with no defined outcome are exactly where bugs hide.
  • Explicitly mark invalid transitions in the table, not just valid ones. Add a column or separate section for "forbidden" rows. If it is not written down, testers assume it was not required and skip it.
  • Check terminal states exhaustively. Every event arriving in a terminal state (Completed, Closed, Paid Out) should be rejected. Run through every defined event against every terminal state — the matrix is usually small and takes minutes.
  • Name your test cases by transition, not by screen. "TC-ST-007: Verified account — fraud-flag event — transitions to Suspended and emails admin" is far more traceable than "Test account suspension".
  • Verify the action, not just the state change. A transition might reach the right next state but fire the wrong email, skip the audit log entry, or fail to update a linked record. Include the expected action in every test case assertion.
  • Test concurrent transitions on shared entities. What happens if two agents process the same order simultaneously? Race conditions between transitions are a common production bug that the basic model doesn't capture — add at least one concurrency test for high-traffic systems.
  • Use 0-switch coverage as a smoke test, not a done criteria. Visiting every state once tells you the basic paths exist. Ship it as a sanity check but never mark state transition testing complete at 0-switch.
  • Escalate to 2-switch on safety-critical or compliance paths. For anything touching money, health, or legal status in NZ, consecutive transition pairs matter — the sequence Approved → Paid Out → Archived has different risks than Approved → Archived alone.
  • Review the transition table with a developer before testing. A 15-minute walkthrough catches states the dev knows about but never documented, and aligns on what "rejected gracefully" means for each invalid case.
  • Include invalid-transition test results in your exit report. "All-transitions coverage achieved" means nothing to a stakeholder. Show the count of valid transitions tested, invalid transitions tested, and the system's rejection behaviour for each — especially for auditable NZ government or financial systems.

7 Common Misconceptions

❌ Myth: If all the valid transitions pass, state transition testing is done.

Reality: Testing only valid transitions is half the job — and arguably the less important half. The most damaging production bugs in state-based systems are invalid transitions that were silently accepted: a cancelled order that still dispatched, a closed account that still accepted payments. All-transitions coverage includes a deliberate test for every invalid combination, verifying the system rejects it with a proper error and leaves state unchanged.

❌ Myth: You need a formal state diagram before you can apply this technique.

Reality: The transition table is the working artifact, not the diagram. Senior testers often build the table directly from requirements, user stories, or code inspection — the diagram is useful for communication but is not a prerequisite. Start with a whiteboard column of "what states can this thing be in?" and fill in the events from there. The formality scales with the risk, not the process.

❌ Myth: State transition testing is only for workflows with many steps — small systems don't need it.

Reality: Even a two-state toggle (Active/Inactive) has invalid transition cases: what happens if you try to activate an already-active account, or deactivate it twice in a row? Simple systems have fewer transitions but the same classes of bug. The technique scales down to a five-minute check — just build a tiny table and verify the impossible events are rejected.

8 Now You Try

Three graded exercises — spot, fix, then build. Write your answer, run it for AI feedback, then compare to the model answer.

🔍 Exercise 1 of 3 — Spot: find the invalid transitions

A RealMe-style account has four states: Unverified, Pending, Verified, Suspended. The valid transitions are: Unverified→Pending (submit docs), Pending→Verified (docs accepted), Pending→Unverified (docs rejected), Verified→Suspended (fraud flag), Suspended→Verified (cleared). Identify three transitions that should be invalid and say what the system must do when each is attempted.

Show model answer
Any three of these well-explained earn full marks:

1. Unverified --jump straight to Verified--> Verified. No documents were ever submitted. The system must reject it; identity cannot be confirmed without the Pending review step.
2. Verified --submit documents--> Pending. Re-submitting docs for an already-verified account should be a no-op or rejected, not a silent move back to Pending that drops verified status.
3. Suspended --self-clear fraud--> Verified. A user cannot lift their own suspension; only an admin event (fraud cleared) may do that.
4. Unverified --fraud flag--> Suspended. There is nothing verified to suspend.

In every case the rule is the same: the system must reject the transition gracefully — show an error or ignore it — and must NOT silently change state or corrupt the record. Silently accepting an impossible event is the bug.
🔧 Exercise 2 of 3 — Fix: repair a broken transition table

A tester drafted the transition table below for a NZ online vehicle-registration renewal (states: Draft, Submitted, Paid, Complete). It is broken: one row has an impossible transition marked valid, and one genuinely valid transition is missing. Rewrite the table so it lists only valid transitions and add the missing one.

Flawed table:
Draft — submit → Submitted (valid)
Submitted — pay → Paid (valid)
Complete — pay again → Paid (valid) ← suspicious
Paid — (nothing listed) ← something missing?

Rewrite as a correct transition table:

Show model answer
Correct valid-transition table:
1. Draft --submit--> Submitted
2. Submitted --pay--> Paid
3. Paid --confirmation issued--> Complete

The impossible transition I removed: "Complete --pay again--> Paid". Once a renewal is Complete it is a terminal state; paying again must be rejected, not allowed to move backwards to Paid. Marking it valid would let a finished renewal be reopened and re-charged.

The valid transition I added: "Paid --confirmation issued--> Complete". The original table had no way to reach the Complete state at all, so the renewal could never finish. Every non-terminal state needs at least one outgoing valid transition.
🏗️ Exercise 3 of 3 — Build: a state model for a KiwiSaver withdrawal

Design a complete state model for a KiwiSaver first-home withdrawal application. Define the states, list every valid transition (from state, event, to state), and name at least two invalid transitions the system must reject. Aim for all-transitions (1-switch) coverage.

Show model answer
A strong model (yours may differ in naming but should have the same shape):

States: Draft, Submitted, Under Review, Approved, Declined, Paid Out.

Valid transitions:
1. Draft --submit application--> Submitted
2. Submitted --provider begins review--> Under Review
3. Under Review --evidence accepted--> Approved
4. Under Review --evidence insufficient--> Declined
5. Approved --funds released--> Paid Out
6. Declined --applicant resubmits--> Submitted

Invalid transitions to reject:
1. Submitted --funds released--> Paid Out. Cannot pay out before review and approval; must be rejected.
2. Paid Out --resubmit--> Submitted. Paid Out is terminal; a completed withdrawal cannot be reopened.
3. (bonus) Declined --funds released--> Paid Out. A declined application must never pay out.

All-transitions coverage means one test per valid transition (six tests), plus explicit tests that each invalid transition is rejected gracefully without corrupting state.

Why teams fail here

  • They stop at 1-switch and call it done. All-transitions coverage confirms every valid transition fired once — it says nothing about what the system does when an impossible event arrives. Teams tick the ISTQB box and ship, leaving every terminal state untested against every forbidden event.
  • They trust the UI to enforce state rules. The UI button is greyed out, so the transition "can't happen". But batch jobs, admin tools, second microservices, and a KiwiSaver provider's nightly reconciliation job all call the same API endpoint without touching the UI. If the server doesn't validate, the state machine has no actual enforcement.
  • They model only the states in the requirements doc. Legacy NZ systems — CoverNZ claims, visa application portals, Benefits NZ benefit workflows — accumulate ghost states: status values that exist in the database from old migrations or manual SQL patches but appear nowhere in documentation. A tester who builds the table from the spec and never runs SELECT DISTINCT status against the real data will miss those states entirely.
  • They omit the action from the assertion. A transition test that only checks "next state = X" misses half the contract. A KiwiSaver fund switch that lands in the correct state but skips the member notification email, fails to write the audit log, or neglects to update the unit registry is still broken — you just can't see it by checking the status column alone.

Key takeaway

Draw the column of events that must be impossible in every state — and then test every cell in it, because that is where production incidents live, not in the transitions you already documented.

How this has changed

The field moved. Here is how State Transition Testing evolved from its origins to current practice.

1950s

Finite state machines (FSMs) formalised in computer science by Moore and Mealy. The mathematical foundation for state transition testing predates software QA as a discipline.

1970s–80s

Myers and Beizer apply FSM theory to software testing. State transition diagrams become a standard test design technique for UI flows, embedded systems, and protocol testing. Coverage criteria (state coverage, transition coverage, path coverage) are defined.

1990s

ISTQB certifies state transition testing as a core black-box technique. Applied widely in telecoms protocol testing (where state machines are explicit in standards like GSM and SIP), embedded systems, and transaction-based systems.

2000s

Web applications with session management and multi-step workflows bring state transition testing to mainstream web QA. Shopping carts, checkout flows, and authentication states are natural state machine candidates.

Now

AI systems are inherently stateful in ways that traditional state transition testing struggles to capture — conversational context, memory, tool state. Model-based testing tools can generate state transition tests from OpenAPI specs and UI models automatically. AI testing requires extending the state machine metaphor to include non-deterministic state transitions.

Self-Check

Click each question to reveal the answer.

Q1: What are the four elements of a state transition model?

States (the distinct conditions the system can be in), events or triggers (inputs that cause a change), transitions (the move from one state to another), and actions (what the system does when a transition occurs).

Q2: Name the three main coverage levels and which one is the ISTQB Foundation standard.

0-switch (all states — visit every state once, weakest), 1-switch (all valid transitions — the ISTQB Foundation standard), and 2-switch (all transition pairs — strong but expensive). 1-switch / all-transitions is the expected baseline.

Q3: Why is testing invalid transitions just as important as testing valid ones?

Because the worst bugs are events arriving in a state where they should be impossible — a delivered parcel scanned back to in-transit, a completed payment re-charged. If the system silently accepts these, it corrupts its own data. You must verify it rejects them gracefully.

Q4: What does it mean for a state to be "terminal", and what must you check about it?

A terminal state (e.g. Completed, Paid Out, Closed) has no valid outgoing transitions — the workflow ends there. You must check that every event arriving in a terminal state is rejected, so a finished item cannot be reopened, re-charged, or moved backwards.

Q5: How do you turn a state diagram into test cases?

Convert the diagram (boxes = states, arrows = transitions) into a transition table listing current state, event, next state, and action. Each valid row becomes a test case for all-transitions coverage, and each impossible state/event pairing becomes an invalid-transition test.

Q6: Your team is testing an Benefits NZ benefit application portal. The application can be in states: Draft, Submitted, Under Assessment, Approved, Declined, and Closed. A sprint review reveals testers only confirmed the happy path (Draft through to Approved). What coverage gaps should you raise and why?

A: The team has 0-switch coverage at best — they have visited several states but have not exercised every valid transition (e.g. Submitted back to Draft if incomplete, Under Assessment to Declined, Approved to Closed). More critically, they have no invalid-transition tests: can a Declined application be directly re-Approved without resubmission? Can a Closed application receive an Approved event? For a government benefits system these gaps carry compliance risk — Benefits NZ auditors expect evidence that prohibited state changes are rejected, not just that the happy path works.

Q7: What is the key difference between state transition testing and decision table testing, and how do you decide which to use?

A: Decision table testing maps combinations of conditions to outcomes at a single point in time — it is stateless. State transition testing models behaviour that depends on what has happened before — the same event produces different outcomes depending on which state the system is currently in. Use a decision table when you have multiple independent conditions affecting one output (e.g. discount eligibility rules). Use state transition when the system has a lifecycle and prior history matters (e.g. an CoverNZ claim moving through Lodged, Assessed, Approved, Closed). Many real systems need both: a decision table for the approval logic within a state, and a state model for the transitions between states.

Q8: A developer says "We don't need to test invalid transitions — the UI prevents users from triggering them, so they can't happen." What is wrong with this reasoning and how do you respond?

A: UI guards are not a substitute for server-side state validation. A malicious user can bypass the UI entirely by calling the API directly, replaying requests, or manipulating hidden form fields. More commonly, other systems (batch jobs, admin tools, database patches, or a second microservice) may call the same backend endpoint without going through the UI. In NZ financial and government systems — Revenue NZ, TransitNZ, KiwiSaver providers — backend state enforcement is a compliance requirement, not optional. The correct response is: the server must validate and reject invalid transitions regardless of what the UI allows, and you must test that rejection by calling the endpoint directly, bypassing the frontend.

Q9: When should you NOT use state transition testing, and what technique should you reach for instead?

A: Avoid state transition testing when the system has no persistent lifecycle — a stateless calculation endpoint, a search filter, or a read-only report has no states to model and a transition table adds no value. Skip it when there are only two states (enabled/disabled) and a simple checklist covers both; or when the "states" are really just UI screens with no status persisted to a database (use exploratory charters or use-case testing instead). It is also a poor fit when the number of possible states is effectively unbounded, such as a free-text field — equivalence partitioning or boundary value analysis will find more bugs faster. In time-pressured sprints where the system already has passing unit tests for state logic, note the risk, point to the unit tests as partial coverage, and spend the remaining time on exploratory testing of edge cases.

Interview Questions

What NZ hiring managers ask about State Transition Testing — and what strong answers look like at each level.

Q: What is state transition testing, and why would you use it on a feature like a KiwiSaver withdrawal application?

Strong answer: State transition testing models a system as a fixed set of states and the events that move it between them, then tests every valid transition at least once and explicitly tests that invalid ones are rejected. You’d use it on a KiwiSaver withdrawal because the application has a defined lifecycle — Draft, Submitted, Under Review, Approved, Paid Out — and getting a transition wrong could mean paying out a declined application or letting a completed withdrawal be reopened. Any feature with a named lifecycle and real consequences for incorrect state changes is a good candidate.

Grad / Junior

Q: What are the three coverage levels in state transition testing, and which one would you target as a minimum for an Revenue NZ tax return workflow?

Strong answer: The three levels are 0-switch (visit every state at least once), 1-switch or all-transitions (exercise every valid transition at least once — the ISTQB Foundation standard), and 2-switch (every consecutive pair of transitions). For an Revenue NZ tax return workflow I’d target 1-switch as the minimum because it confirms every defined lifecycle path works, and I’d add explicit invalid-transition tests on top of that — Revenue NZ auditors want evidence that forbidden state changes were attempted and rejected, not just that the happy path passed.

Junior

Q: A developer tells you the UI prevents users from triggering invalid transitions, so you don’t need to test them. How do you respond?

Strong answer: UI guards are not a substitute for server-side validation. Anyone can bypass the frontend by calling the API directly, replaying requests, or manipulating form data — and internal systems like batch jobs, admin tools, or a second microservice will often call the same backend endpoint without going through the UI at all. In NZ government and financial systems — think CoverNZ claims or Benefits NZ benefit portals — backend state enforcement is a compliance requirement. My response is: the server must validate and reject invalid transitions regardless of the UI, and I’ll test those rejections by calling the endpoint directly to confirm the system returns an error and leaves state unchanged.

Senior

Q: When would you NOT use state transition testing, and what technique would you reach for instead?

Strong answer: I’d skip it when the system has no persistent lifecycle — a stateless calculation endpoint or a read-only report has no states to model, so the technique adds no value. I’d also avoid it when there are only two states like enabled/disabled (a simple checklist is faster), when the states are really just UI screens with nothing persisted to a database (use exploratory charters or use-case testing), or when time is very tight and unit tests already cover the state logic (note the risk and move to exploratory testing). The key signal is: if there is no status column in a database, there is probably no state machine to test.

Senior

Q: Your team inherited an CoverNZ claims system with no state documentation. A new sprint adds a “Reopen” event to Closed claims. How do you approach testing it, and how do you get the rest of the team using state transition testing on a codebase like this?

Strong answer: First I’d reverse-engineer the existing state model by reading the code, querying the status column for distinct values, and spending 30 minutes with the developer — the diagram comes after the investigation. For the Reopen event I’d build a transition table covering all states, identify which ones should be able to reopen and which are terminal, then write tests for both the valid Reopen path and the cases that must be rejected. To get the team on board I’d make the table visible — add it to the Confluence page for that domain, reference it in sprint planning when new events are discussed, and include invalid-transition test counts in the definition of done for any state-changing feature. Showing the first production bug caught by an invalid-transition test does more for adoption than any training session.

Lead

Q: What is the difference between state transition testing and decision table testing, and how do you decide which to apply?

Strong answer: Decision tables map combinations of conditions to outcomes at a single point in time — they are stateless. State transition testing models behaviour that depends on what has happened before: the same event produces a different outcome depending on current state. I’d use a decision table for something like discount eligibility rules where multiple independent conditions combine to produce one output. I’d use state transition testing for any feature with a lifecycle — an TransitNZ vehicle registration, a RealMe identity verification, a TransitNZ permit application. In practice many systems need both: a decision table for the approval logic inside a state, and a state model for the transitions between states.

Senior

Enterprise reality

Enterprise workflow systems with regulatory requirements around state integrity

  • State diagrams become formal design artefacts — in regulated enterprises (banks, insurers, government integrators) the state model is approved by architects and signed off by compliance before development begins, not reverse-engineered from code after the fact.
  • Illegal state transitions trigger security alerts, not just test failures — in high-value workflows (KiwiSaver disbursements, CoverNZ claim payments, Revenue NZ refunds) an unexpected state change at the API layer fires a SIEM alert and may freeze the record pending investigation, because it is treated as a potential fraud signal rather than a simple validation error.
  • State audit logs are tested as a first-class concern — every transition must write an immutable audit entry (who, what, when, from-state, to-state). Testing that the audit log is correct and tamper-evident is as important as testing that the status column updated correctly; regulators audit the log, not the UI.
  • Compensating transactions are tested for every state that can fail mid-transition — in distributed enterprise systems a transition may span a database write, a message queue publish, and a downstream API call. If any step fails mid-flight, the compensating transaction must return the record to its prior state cleanly. Testing that rollback is reliable is a separate test concern from testing the happy-path transition.

What I would do

Professional judgment — when to reach for State Transition Testing, when to skip it, and what to watch for.

If…
I am testing a KiwiSaver provider portal where a withdrawal application moves through Draft → Submitted → Under Review → Approved → Paid Out, and incorrect transitions could trigger a double payout or reopen a completed disbursement
I would…
Build the full transition table in the first hour of the sprint, walk it with the developer to confirm terminal states, then write all-transitions (1-switch) tests for valid paths and a separate test suite for every invalid transition at terminal states. I would bypass the UI and call the API directly for the invalid tests, then attach the rejection evidence to the test report for the auditor.
If…
I inherit an CoverNZ claims system with no state documentation and a developer who estimates the status column has “maybe six or seven values, not sure exactly”
I would…
Run SELECT DISTINCT status FROM claims against the test database before writing a single test case. Whatever values come back are my states — including any legacy values that should no longer exist. Build the table from those real values, not from the documentation that was never written. The diagram can come after; the table comes first.
If…
The sprint is tight and a developer tells me unit tests already cover all the state logic — so I should skip state transition testing and move on to other features
I would…
Accept partial coverage, but not blindly. Unit tests cover logic in isolation; they rarely test what happens when a batch job, admin tool, or a second microservice hits the same endpoint with a stale or corrupted status value. I would spend 20 minutes writing targeted invalid-transition tests against the API directly for the two or three terminal states where the consequences of failure are highest, note the risk in the test report, and move on.

The bottom line: Reach for State Transition Testing the moment you see a status column in a database or a lifecycle in the requirements — but spend the majority of your effort on the transitions that should never happen, because those are the ones that will cost you at 2 a.m. on a Sunday.