State Transition Testing for Wizards, Carts, and Onboarding

Written By  Crosscheck Team

Content Team

July 27, 2026 9 minutes

State Transition Testing for Wizards, Carts, and Onboarding

State transition testing for wizards, carts, and onboarding

A customer reached the payment step of a four-step checkout, changed her mind about the delivery address, and pressed the browser back button. She fixed the address, moved forward again, and paid.

The order shipped to the old address. The address screen had saved the new value, but the order had already been created at the payment step with the old one.

Nobody had tested going backwards. Every test in the plan walked forward, once, in a straight line.

Short version

  • A state is a situation the system is sitting in, such as "cart has items" or "payment pending".
  • A transition is a move between states, caused by an event such as a click or a timeout.
  • Draw the states and moves first, then test the moves nobody planned for.
  • The back button, a page refresh, and walking away are events, not accidents.
  • Also test moves that should be impossible, such as reaching the payment page with an empty cart.

The four words you need

State — a situation the system is in and stays in until something happens. "Cart empty", "awaiting payment", "order confirmed".

Event — something that happens: a button click, a timeout, a payment webhook, a browser refresh.

Transition — the move from one state to another, caused by an event.

Guard — a condition that must be true for a transition to be allowed. For example, you can only move to Payment if the cart total is above zero.

State transition testing means listing all four, then checking that the system follows its own rules.

The flow we will map

A standard checkout at https://staging.example.com/checkout:

  Empty cart
      | add item
      v
  Cart has items  <---- edit cart ----+
      | continue                      |
      v                               |
  Delivery details -------------------+
      | continue
      v
  Payment pending
      | payment succeeds        | payment fails
      v                         v
  Confirmed                 Payment failed
                                | retry
                                v
                            Payment pending

Plus two states that no wizard diagram ever seems to include:

  • Abandoned — the user left. Reached from any state after 30 minutes of inactivity.
  • Expired — the reserved stock was released. Reached from Payment pending after 15 minutes.

Adding those two states to the drawing is often the single most valuable thing you do all sprint.

Drawing the diagram, step by step

  1. List the states. Name them as situations, not screens. "Payment pending" is a state; "the payment page" is a screen. One state can show several screens.
  2. List the events. Include clicks, but also timeouts, refreshes, back navigation, session expiry, and messages arriving from other systems.
  3. Draw an arrow for each valid transition and label it with the event.
  4. Mark the guards on the arrows that have conditions.
  5. Mark the start and end states. Every flow has at least one of each.
  6. Ask, for each state, what happens on refresh and on back. Add the arrows you discover.

Step 6 is where the real work is. Most diagrams stop at step 3.

The state table

Turn the diagram into a grid. States down the side, events across the top, and the resulting state in each cell. Write invalid where the move should not be allowed.

StateAdd itemContinueBackRefresh15 min timeout
Empty cartCart has itemsinvalidEmpty cartEmpty cartEmpty cart
Cart has itemsCart has itemsDelivery detailsCart has itemsCart has itemsAbandoned
Delivery detailsCart has itemsPayment pendingCart has itemsDelivery detailsAbandoned
Payment pendinginvalidinvalidDelivery detailsPayment pendingExpired
Payment failedinvalidPayment pendingDelivery detailsPayment failedAbandoned
ConfirmedCart has itemsinvalidConfirmedConfirmedConfirmed

Every cell is a test. The invalid cells matter most, because they are the ones no developer wrote code for. Try to reach the payment page directly with an empty cart by opening https://staging.example.com/checkout/payment in a fresh tab, and see what the system does.

The bottom row is worth reading twice. After an order is confirmed, pressing back must not return to Payment pending. That is how orders get charged twice.

Testing the back button

The back button is an event with no button of its own, so it never appears on a design mockup. Test it from every state.

For each state, press back once and check three things:

  • Which state are you in now?
  • Is the data on screen the current data, or a cached copy from before?
  • If you now press forward or continue, does the system use the new data or the old data?

The third check is the one that found the wrong shipping address. The screen looked right and the stored order was stale.

Bad: "Verify the back button works."

Good: "From Payment pending, press back. Expect Delivery details with the saved address 12 Oak Road. Change it to 8 Elm Street, continue, and confirm the order summary shows 8 Elm Street."

Also test the back button twice in a row, and back followed immediately by forward. Browsers restore pages from a cache, so the second press often behaves differently from the first.

Testing refresh mid-flow

Press F5 in every state and check whether you stay where you were.

Three specific cases to run:

  1. Refresh during a submit. Press continue and refresh while the spinner is still on screen. Look for a duplicate record.
  2. Refresh after a form post. If the browser asks to resend form data, that is usually a design problem, not a browser problem.
  3. Refresh in a new tab. Open the same flow in a second tab and refresh the first. Two tabs sharing one session is a common source of state that does not match.

Refresh bugs are quiet. Nothing turns red; you simply end up in the wrong state with a half-saved record. Because there is no visible error, capture what the browser saw at that moment — the console output and the failed request tell the story that the screenshot cannot. A browser-based reporter such as Crosscheck collects all three from the page you are on, which matters when the bug only appears once in ten attempts.

Testing abandoned and expired states

Users leave. That is not an edge case, it is the most common ending for any wizard.

  • Start the flow, leave the tab open for the full timeout, then come back and click continue. Expect a clear message, not a stack trace.
  • Start the flow, close the tab, then return through the same link. Are the entered values still there? Should they be?
  • Let a payment reservation expire, then complete the payment anyway. Was the stock released? Is it now oversold?
  • Check what happens to a partly filled record in the database. A user stuck in status: pending forever will confuse the support team later.

To test timeouts without waiting, ask a developer to lower the value on staging, or move your machine clock forward. Both are faster than waiting 30 minutes.

Choosing how much to cover

Two levels of coverage cover almost every real need.

Every transition once, sometimes called 0-switch coverage. Walk each arrow on the diagram at least one time. This is the minimum and it already catches most invalid-move bugs.

Every pair of transitions, sometimes called 1-switch coverage. Walk each arrow, then every arrow that can follow it. This finds bugs that only appear in a sequence, such as back-then-continue or fail-then-retry.

Start with every transition once. Add pairs for the parts that touch money, stock, or permissions.

Frequently asked questions

When is state transition testing worth the effort?

Whenever the system remembers something between screens: checkouts, multi-step forms, onboarding, approval flows, subscriptions, and anything with a status field.

How is it different from a decision table?

A decision table covers combinations of conditions at a single moment. State transition testing covers order and history — what happened before matters.

Do I need special software to draw the diagram?

No. A whiteboard photo or a plain text sketch like the one above is enough. The value is in the thinking, not the tool.

How do I find the states if there is no documentation?

Look at the status values in the database or the API responses. A field named status with values such as draft, pending, paid, and cancelled is your list of states.

Should invalid transitions be tested through the interface or the API?

Both. The interface usually hides the invalid move by disabling a button, while the API often still accepts the request. The API is where the real rule should live.

Related Articles

Contact us
to find out how this model can streamline your business!

Trusted by thousands ofengineering teams worldwide.

Add to Chrome
200+ reviews · 100k+ users
Crosscheck browser extension capture controls

Join the Crosscheck Community

Stay in the loop with Crosscheck's newest features and insights.