State Based Testing
Quick Summary
Use the STAR-A mnemonic to help you remember what to test:
State - Every state can be reached
Transitions - All transitions work correctly
Actions - Actions in each state are valid and correct
Repeat - Repeat states/actions to find sequence-dependent bugs
Advanced
Interrupt transitions (abort, cancel, restart, make it fail, let it time out)
Revisit states and repeat transitions multiple times to find sequence-dependent bugs
Vary event triggers: user, external, internal, passage of time
Consider a combination of 1-switch or 2-switch coverage + Risk-based, Travelling Salesman or Chinese Postman paths.
Use cases for State Models
Creating a state model is common because state is everywhere - in both software and real life. Actions often depend on the current state, and these must be tested.
"High-level" or business examples:
Shopping cart: Empty → Items added → Checked out → Order placed
Ticket lifecycle: Open → In progress → Resolved → Closed → Reopened
ATM: Insert Card → Enter Pin → Withdraw Cash
"Low-level" or technical examples:
Service / API Lifecycle: Idle → Processing → Response Sent
Authentication: Logged out → Logged In → Session Expired
File Upload: Idle → Selecting File → Uploading → Verifying → Success / Error
Sample Models
Basic Template
A state leads to another state
A transition may lead back to the same state (self-loop, e.g., invalid input leads back to the same input prompt)
Composite States are high-level states that group related, low-level states
A choice (diamond) may lead to two or more paths and states
A fork may split or join paths
Elementary examples
You might notice that the above diagram doesn't explicitly cover:
The card may be ejected if it's invalid
The card may be swallowed after three consecutive incorrect PINs
All the possible transactions (withdraw, deposit, view balance, other) - instead, all transactions are bundled into a single transition
Test data (rarely represented in State Diagrams. Instead, complementary models and techniques are developed):
PIN: correct/incorrect values
Card types: supported/not supported
Withdrawal: defective input, sufficient/insufficient balance
etc.
Important!
The main takeaway of the above ATM example is that:
A state diagram does NOT have to represent the entire system. Even a simple diagram like the one for an ATM can become huge if fully mapped.
Diagrams can be more or less abstract depending on your needs - one must consciously choose the level of detail.
If you search for "ATM state diagram," you’ll find many different versions. There’s rarely one “correct” diagram.
Resources to learn State Diagrams more deeply:
Creating the Model
To create a state model:
Compile the system's unique states
[state]and all the transitions-->between themDecide on the level of abstraction (more or less detail) and understand the trade-offs
Identify events causing the transition
Define what happens during each transition
Ways to cover the graph
It's very challenging and often impractical to cover all combinations of all transitions (including transition pairs, triples, etc.). Consider the following ways to cover the entire State Diagram:
Happy Path: most used routes (don't guess, gather reliable information)
Risk-based Path: less common, but of critical importance
Travelling Salesman's route: all states in one test case at least once
Chinese Postman Route: all transitions (back and forth, if possible) at least one
Round-Trip: cover at least one complete cycle returning to the same state
The above variants prioritize maximum coverage in the shortest possible time (or with fewer actions taken). But this isn't necessarily robust testing.
Upgrading to N-Switch Coverage
In state-based testing:
0-switch coverage → each test covers a single transition (no chaining of transitions)
1-switch coverage → each test covers pairs of consecutive transitions (2 transitions, 3 states)
2-switch coverage → each test covers a triple of consecutive transitions (3 transitions, 4 states)
N-switch coverage → each test covers sequences of N+1 consecutive transitions. Greater N leads to more robust testing, but it means exponentially more test cases.
Given the State Diagram below, we can produce a 0-switch coverage table:
S1
{some action}
S2
S2
{some action}
S3
S3
{some action}
S4
S4
{some action}
S3
And the 1-switch coverage table (aka transition pairs: 2 transitions, 3 states):
S1
{some action}
S2
{some action}
S3
S2
{some action}
S3
{some action}
S4
S3
{some action}
S4
{some action}
S3
S4
{some action}
S3
{some action}
S4
Choosing Coverage Level
A: Both 0-switch and 1-switch (and any N-switch) allow covering all transitions and states. So how does greater N leads to more robust testing?
B: Greater transition sequences help catch bugs that only appear when certain transitions happen in sequence - e.g., state-dependent side effects or incorrect state updates after a chain of actions. (See "Advanced State Testing" below).
A: So let's choose greater N and go with 2-switch or even 3-switch testing.
B: 2-switch or greater become exponentially more complex (similar to adding conditions to a Decision Table). 1-switch is sometimes considered a practical baseline that can be complemented with:
Pairwise or combinatorial testing
Advanced State Testing
1. Interrupting Transitions
Creating/deleting, processing, verifying, uploading, downloading, importing/exporting - verbs in continuous tense are generally represented as transitions, not states. And they can be:
aborted (and restarted)
interrupted (and resumed)
timed out (and restarted)
failed (and retried)
All of these can be triggered and tested. Additionally, such actions uncover further basis for testing:
Partial state persistence (if applicable): stop the system mid-transition, then restart:
Does the system remember partial progress?
Is the data corrupted after the progress finishes?
Are there undesired junk/leftover files from the first attempt?
Re-entrant actions: trigger the same action while it's in progress.
Error state recovery: entering a fault or error state, then confirm valid exit paths exist and function correctly.
2. Finding sequence-dependent bugs
Advanced testing involves revisiting states and repeating actions multiple times in the same or different sequences - some defects become apparent only after repeating certain steps (and sometimes in a specific sequence). This may be caused by:
Cache state - Old or stale data is retained between operations, leading to incorrect results when actions are repeated without clearing or refreshing the cache.
Resource leaks - Memory, file handles, or connections aren’t properly released, so repeating actions depletes resources and causes failures over time.
Cumulative state corruption - Small, unnoticed data inconsistencies build up with each repetition until they cause incorrect behavior or crashes.
3. Varying event triggers
User actions are not the only thing that can trigger an event. Consider the following to vary your testing efforts:
Externally generated events: those that come from outside the System Under Test (SUT). Typically, other systems or modules that your SUT integrates with.
System-generated events: those triggered by the SUT itself. Often, these events are the result of the system completing some background activity.
Passage of time: scheduled job, timeout, start of day, or other regular interval
Free Browser Tools for UML Diagrams
PlantUml: https://plantuml.com/state-diagram
Mermaid Diagrams and Charts: http://mermaid.js.org/
Further Resources
Testing Software Design Modeled by Finite-State Machines, Tsun S. Chow
Last updated