State Transition Testing
State transition testing is a black-box test design technique for behaviour that depends on history. You model the system, or one thing it manages such as an account, an order or a document, as states and the events that move it between them, then derive test cases from that model. In sign-in with a one-time code, the account is Signed out, Code sent, Signed in or Locked: a wrong code keeps the user at Code sent the first two times and locks the account the third time. The same input gives a different result because the state is different.
Equivalence partitioning grouped input values that the system should treat alike. A state model does the same for histories: instead of trying every order of clicks, you test the few states the system can be in, the allowed moves between them, and the refusal of forbidden moves.
Why it matters
Many serious defects appear only after a particular history: a cancelled order that still ships, a locked account that accepts the correct code, a session that survives sign-out. Each input is valid on its own, so techniques that examine one input at a time miss them.
A state model turns a workflow into a finite list of coverage items, states and transitions, so you can say exactly what was tested. Building it also exposes gaps early: every combination of state and event that nobody specified becomes a question for the product owner, not a failure in production.
States, events, transitions and guards
A state is a condition the modelled object stays in until something happens. An event is what happens: a click, an incoming message, a timer running out. A transition is the change of state that an event causes; ISTQB treats it as instantaneous. It may cause an action, such as sending an SMS, and may carry a guard condition that must be true for the transition to fire.
Transitions are labelled event [guard] / action, leaving out parts that do not exist. Guards matter when one event can lead to different states: "wrong code [1st or 2nd wrong attempt]" keeps the user at Code sent, while "wrong code [3rd wrong attempt]" locks the account. A guard such as an attempt count is a boundary, so test both sides of it. The examples use this requirement:
- The user requests a code; the system sends a 6-digit code by SMS, valid for 5 minutes.
- The correct code signs the user in, and a code works only once. Signing out ends the session.
- After a wrong code the user may try again; the third wrong code in a row locks the account for 30 minutes, after which the user can request a new code.
The state diagram and the state table
A state diagram, also called a state transition diagram, draws states as boxes and valid transitions as labelled arrows. For the sign-in requirement it has four states, S1 to S4, and seven transitions, T1 to T7. It is easy to review with the team, but it shows only what is allowed.
| # | From | To | Event [guard] / action |
|---|---|---|---|
| 1 | S1 Signed outStart | S2 Code sent | E1 request code / send a 6-digit code by SMS |
| 2 | S2 Code sent | S3 Signed in | E2 correct code / open a session |
| 3 | S2 Code sent | S2 Code sent | E3 wrong code [1st or 2nd wrong attempt] / show the attempts leftalternative path |
| 4 | S2 Code sent | S4 Locked | E3 wrong code [3rd wrong attempt in a row]alternative path |
| 5 | S2 Code sent | S1 Signed outStart | E4 code expires (5 minutes after sending)alternative path |
| 6 | S3 Signed in | S1 Signed outStart | E5 sign out / end the session |
| 7 | S4 Locked | S1 Signed outStart | E6 lock ends (30 minutes later)alternative path |
Only valid transitions are drawn. The state table below shows the other state and event pairs: the invalid transitions, and one gap the requirement leaves open.
A state table holds the same model as a grid: one row per state, one column per event, and in each cell the transition and its target state. It also shows what is forbidden: an empty cell, a dash below, is an invalid transition, where the system should refuse or ignore the event and stay in its state. The ISTQB syllabus gives each event and guard pair its own column, so E3 would take two; this table keeps both guards of E3 in one cell to stay narrow.
Filling the table reveals gaps. The requirement does not say what happens if the user requests another code while the first is still valid, so the cell S2 × E1 holds a question for the product owner, not a test.
E1 E2 E3 E4 E5 E6
request correct wrong code sign lock
code code code expires out ends
S1 Signed out T1: S2 - - - - -
S2 Code sent ? T2: S3 T3: S2 [1st,2nd] T5: S1 - -
T4: S4 [3rd]
S3 Signed in - - - - T6: S1 -
S4 Locked - - - - - T7: S1
- invalid transition: refused, the state does not change
? not specified in the requirement: ask the product ownerCoverage: all states, valid transitions, all transitions
A test case here is a sequence of events from a known start state, with the expected state and action after each event; one test case usually covers several transitions. The ISTQB Foundation syllabus discusses three coverage criteria. Each is stronger than the one before it: 100% of a later criterion guarantees 100% of the earlier ones.
- All states coverage: the coverage items are the states. Two test cases reach 100%, E1, E2 and E1, E3, E3, E3, yet they exercise only T1 to T4: 4 of 7 valid transitions (57%). Code expiry, sign-out and the end of the lock stay untested.
- Valid transitions coverage, also called 0-switch coverage: the coverage items are the single valid transitions, T1 to T7. It is the most widely used criterion, and reaching 100% also exercises every state.
- All transitions coverage: the coverage items are all transitions in the state table; every valid one is exercised and every invalid one attempted. The syllabus recommends it as a minimum for mission- and safety-critical software.
Worked example: test cases for the one-time code
For valid transitions coverage, start each test case in S1 and chain events until every transition has been exercised. Three test cases are enough. One long sequence could cover all seven, because T5, T6 and T7 all return to S1, but then a failure early in the sequence blocks every transition after it. In TC-03 the first two wrong codes exercise T3, because the guard is true, and the third exercises T4.
Write each expected result as the state and the action, not only the screen message: after the second wrong code the user is still at S2 and sees that one attempt is left. TC-02 and TC-03 wait 5 and 30 minutes, so ask early whether the test environment can shorten these times or set the clock.
Every test case starts in S1 Signed out.
ID Events and resulting states Covers
TC-01 E1 -> S2 E2 -> S3 E5 -> S1 T1 T2 T6
TC-02 E1 -> S2 E4 -> S1 T1 T5
TC-03 E1 -> S2 E3 -> S2 E3 -> S2 E3 -> S4 E6 -> S1 T1 T3 T4 T7
Valid transitions coverage: 7 of 7 (100%)
All states coverage: 4 of 4 (100%)Testing invalid transitions
Of the 17 empty cells in this table, five cannot be triggered: a lock can end only in S4, and an unused code, the only kind that can expire, exists only in S2 and S4. Mark these five as not testable and say why; the other 12 are the invalid transitions to attempt for all transitions coverage.
Attempt one per test case: if a first attempt is wrongly accepted, the system is no longer in the state the next attempt assumes, and one defect hides another (ISTQB calls this defect masking, also known as fault masking). The expected result is that the event is refused and the state does not change; confirm the state with the next event or through the API. Start with the attempts that matter most for security:
- S4 × E2: while locked, enter the correct code before it expires. Expected: still Locked, no session.
- S3 × E2: after signing in, submit the same code again. Expected: refused, one session only.
- S1 × E2: enter a code after it has expired (T5). Expected: refused, still Signed out.
- S4 × E1: request a new code while locked. Expected: no SMS is sent, still Locked.
- S4 × E4: wait for the code to expire while the account is locked. Expected: still Locked; a move to Signed out would let the user request a new code long before the 30 minutes end.
Common mistakes
- Modelling only the happy path and forgetting timeouts, cancellations, expiry and the way out of error states such as Locked.
- Taking 100% all states coverage as enough: in the sign-in model, the two test cases that reach it leave three of the seven transitions untested.
- Never attempting the events a state must refuse, or attempting several in one test case, so the first wrong result hides the others (defect masking).
- Testing a guarded transition on one side of its guard only: the first wrong attempt, but never the third.
- Checking the message instead of the state: a "request refused" message while the order still moves to Shipped is a failure.
The QA perspective
State transition testing starts in test analysis: words such as status, "after", "until", "only when" and "no longer" in a requirement signal a state model. Sketch the diagram, build the table, and take the empty and doubtful cells to the product owner. Ask the developers which transitions the code actually allows; it often differs from the requirement.
Keep the state table with your test cases as testware, with stable ids for states, events and transitions, and report coverage in those terms: "7 of 7 valid transitions, 9 of 12 invalid transitions attempted". A defect report for a failed transition names the start state, the event, the expected state and the actual state.
Practice exercise
Model an expense claim workflow
An expense tool has this requirement: "A claim starts as Draft; its owner can delete it while it is Draft or submit it for review. The manager approves a submitted claim or returns it to Draft with comments. A claim above 5,000 SAR also needs approval by Finance after the manager. Finance pays approved claims."
Build the state model, design the fewest test cases that reach 100% valid transitions coverage, and choose the invalid transitions you would attempt first. Then enter your transitions in the State Transition Generator, one per line such as Draft --submit--> Submitted, and compare its state table and invalid-transition tests with yours.
Show the model answer
States: S1 Draft (start), S2 Submitted, S3 Awaiting Finance, S4 Approved, S5 Paid (end), S6 Deleted (end). Transitions: T1 S1 → S2 submit; T2 S2 → S1 return; T3 S2 → S4 approve [amount ≤ 5,000]; T4 S2 → S3 approve [amount > 5,000]; T5 S3 → S4 Finance approves; T6 S4 → S5 pay; T7 S1 → S6 delete.
Three test cases cover all seven transitions and all six states: TC-01 (1,200 SAR) submit, return, delete — T1, T2, T7; TC-02 (5,000.00 SAR) submit, approve, pay — T1, T3, T6; TC-03 (5,000.01 SAR) submit, approve, Finance approves, pay — T1, T4, T5, T6. The two amounts test both sides of the guard.
Invalid transitions to attempt first, one per test case: pay a claim in S2 (not approved); pay a claim in S5 again (double payment); Finance approves a claim in S2, skipping the manager; delete a claim in S4. Each must be refused, with the state unchanged.
Questions: can Finance return a claim, and to which state? If a returned claim's amount changes, is the guard checked again?
Frequently asked questions
Is a state diagram the same as a flowchart?
No. A flowchart shows the steps of a process in order. A state diagram shows the conditions an object can be in and the events that move it between them, so the same event can lead to different results in different states. Many workflows need both: a flowchart for the user's path and a state model for the object's status.
What is the difference between 0-switch and 1-switch coverage?
0-switch coverage exercises every single valid transition. 1-switch coverage exercises every pair of consecutive valid transitions, such as T3 followed by T2: a wrong code and then the correct one. The three test cases above never run that pair, so they would miss a defect where one wrong attempt invalidates the code. 1-switch needs more test cases and goes beyond the three criteria of the Foundation syllabus. The State Transition Generator can generate test cases for both.
When should I use state transition testing?
Whenever the response to an event depends on what happened before: anything with a status field (orders, claims, tickets), accounts and sessions, subscriptions, multi-step wizards, devices with modes, and API resources whose status changes. When the result depends only on the current combination of inputs, a decision table fits better.
Do I have to test every invalid transition?
Full all transitions coverage requires attempting every invalid transition that can be triggered. Outside mission- and safety-critical software, teams usually choose by risk: the ones a user or an API client can trigger, and those that would move money, data or access. Record the ones you skip and why.
References
- ISTQB Certified Tester Foundation Level (CTFL) v4.0The foundation syllabus of software testing: test levels and types, test design techniques, test management and tools.
- ISTQB GlossaryThe standard definitions of testing terms — test case, regression testing, severity, priority, boundary value analysis and the rest.
Practise with Mutqan tools
Define states and events, then generate a transition table, valid/invalid transition tests and 0-switch/1-switch coverage.
Generate structured test cases (id, title, preconditions, steps, expected result, priority) from a feature description and field list.
Generate boundary values (min-1, min, min+1, nominal, max-1, max, max+1) for numeric and length constraints.
Go deeper
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.