> For the complete documentation index, see [llms.txt](https://practical-testing.gitbook.io/home/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://practical-testing.gitbook.io/home/techniques/state-based-testing.md).

# 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&#x20;

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.&#x20;

\
"**High-level" or business examples**:

* Shopping cart: Empty → Items added → Checked out → Order placed&#x20;
* 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

{% @mermaid/diagram content="stateDiagram-v2
direction LR
state if\_state <<choice>>
state fork\_state <<fork>>
state CompositeState {
InternalState1 --> InternalState2
}

\[\*] --> State1
State1 --> State2
State2 --> State2: self-loop
State2 --> if\_state
if\_state --> CompositeState: if n < 0
if\_state --> State4 : if n >= 0

State4 --> fork\_state:fork
fork\_state --> State5: condition 1
fork\_state --> State6: condition 2

" %}

* 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

{% @mermaid/diagram content="---
title: Example 1 - Lightbulb Switch
-----------------------------------

stateDiagram-v2
direction LR

On --> Off
Off --> On" %}

{% @mermaid/diagram content="---
title: Example 2 - ATM
----------------------

stateDiagram-v2
direction LR

s1 : Idle (wait for card)
s2 : Wait for code
s3 : Wait for transition (withdraw)

s1 --> s2 : Insert card
s2 --> s1 : Wrong PIN
s2 --> s3 : Correct PIN
s3 --> s1 : transaction complete (e.g. withdraw cash)" %}

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.

{% hint style="warning" %}
**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.
  {% endhint %}

\
Resources to learn State Diagrams more deeply:

* <https://sparxsystems.com/resources/tutorials/uml2/state-diagram.html>
* <https://online.visual-paradigm.com/diagrams/tutorials/state-machine-diagram-tutorial/>

### Creating the Model

To create a state model:

1. Compile the system's unique states `[state]` and all the transitions `-->` between them
2. Decide on the level of abstraction (more or less detail) and understand the trade-offs
3. Identify events causing the transition
4. 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

{% @mermaid/diagram content="stateDiagram-v2
direction LR
S1 --> S2
S2 --> S3
S3 --> S4
S4 --> S3" %}

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.

{% hint style="info" %}
**N** refers to the number of *consecutive transitions* in a sequence you want to test
{% endhint %}

Given the State Diagram below, we can produce a **0-switch coverage** table:

| Start State | Event / Transition | End State |
| ----------- | ------------------ | --------- |
| 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):

| Start State | Transition    | Mid State | Transition    | End State |
| ----------- | ------------- | --------- | ------------- | --------- |
| 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](/home/techniques/decision-tables-and-trees.md)). 1-switch is sometimes considered a practical baseline that can be complemented with:
>
> * [Risk-based, Travelling Salesman or Chinese Postman](#ways-to-cover-the-graph) paths
> * [Pairwise](/home/techniques/pairwise-testing.md) or combinatorial testing
> * [Exploratory Testing](/home/testing-practices/perform-exploratory-testing.md)

### 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)

<img src="/files/nc2qfBoiNrhqB6sCilDe" alt="" class="gitbook-drawing">

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

<img src="/files/1ZIzcyh6Gy9kwX1JUE55" alt="" class="gitbook-drawing">

### Free Browser Tools for UML Diagrams

1. PlantUml: <https://plantuml.com/state-diagram>
2. Mermaid Diagrams and Charts: <http://mermaid.js.org/>

### Further Resources

1. [Testing Software Design Modeled by Finite-State Machines](https://archiv.infsec.ethz.ch/intranet_secured/r/1/chow-testingFSMs.pdf), Tsun S. Chow
