Typorithm · easy

6. State transitions and idempotent operations

qatestingtest-designindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

6. State transitions and idempotent operations

0%

Concept and contract

Verify repeated actions and forbidden changes, not just a first successful click.

A transition model describes a starting state, an event and an expected next state. It helps test sequences that a single input-output row cannot explain. A saved role can be absent or present, and save and remove operations should have deliberately defined repeated behavior.

Distinguish a UI toggle from an idempotent API command. Clicking a toggle twice may return to the starting state. Sending the same PUT-save command twice should preserve the saved state under this lab’s contract.

Mechanism and worked example

The worked table covers save and remove from both states.

The worked table covers save and remove from both states. Saving an already saved id keeps it saved, and removing an absent id keeps it absent. This makes retries safe for these particular commands without claiming every operation is idempotent.

A response status alone does not establish the transition. Read the saved list afterward and verify no duplicate id was created. The observable postcondition is the important evidence, especially when a network retry repeats a request.

Complete worked example

json

{
  "cases": [
    {
      "saved": false,
      "action": "save",
      "next": true
    },
    {
      "saved": true,
      "action": "save",
      "next": true
    },
    {
      "saved": true,
      "action": "remove",
      "next": false
    },
    {
      "saved": false,
      "action": "remove",
      "next": false
    }
  ]
}
Reasoning
Each row defines one command’s effect from an explicit state.

Counterexamples and common mistakes

The weak sequence expects a second save to remove the item.

The weak sequence expects a second save to remove the item. That would describe a toggle implementation, contradicting an idempotent save command. A test can be internally consistent yet enforce the wrong contract when event names are ambiguous.

Do not infer that repeated operations have no side effects anywhere. Logging, counters or notifications may still need separate deduplication rules. State exactly which externally meaningful effect is intended to remain stable.

Counterexample: what this check misses

json

{
  "steps": [
    "save r1",
    "save r1"
  ],
  "expectedIds": [],
  "mistake": "Treating save as toggle"
}
Reasoning
A repeated save must not silently become a toggle.

Independent practice: small task

Write a sequence that saves r1 twice, removes it twice, then saves r2.

Write a sequence that saves r1 twice, removes it twice, then saves r2. Give the expected saved ids after each step. Begin with the first two operations and check that only one r1 exists.

Use fresh local state and preserve the initial fixture for other tests. The task has five short transitions. Optional extensions can explore a failed request between steps, but are not needed to understand repeated-command behavior.

Step 1: Save r1 twice and inspect uniqueness. Step 2: Add both removals and the final r2 save.

Practice starter: solve before reading the solution

json

{
  "initialIds": [],
  "steps": [
    {
      "action": "save",
      "id": "r1",
      "expectedIds": "TODO"
    },
    {
      "action": "save",
      "id": "r1",
      "expectedIds": "TODO"
    }
  ]
}
Reasoning
Check the saved id list after the first and second save.
  • Hint 1: Name commands precisely rather than using click as the event.
  • Hint 2: Use fresh state for this sequence.

Solution, tests and trade-offs

The solution records one r1 after either save, no ids after either removal, and r2 after the final save.

The solution records one r1 after either save, no ids after either removal, and r2 after the final save. The verifier runs the sequence and rejects a mutant that implements save as toggle. That establishes a concrete reason for each expected state.

Keep action and expectation paired in the report. A final-only assertion might miss a temporary duplicate or an incorrect intermediate state that later operations conceal. Choose intermediate checks according to the risk rather than adding them mechanically everywhere.

Practice solution and acceptance checks

json

{
  "initialIds": [],
  "steps": [
    {
      "action": "save",
      "id": "r1",
      "expectedIds": [
        "r1"
      ]
    },
    {
      "action": "save",
      "id": "r1",
      "expectedIds": [
        "r1"
      ]
    },
    {
      "action": "remove",
      "id": "r1",
      "expectedIds": []
    },
    {
      "action": "remove",
      "id": "r1",
      "expectedIds": []
    },
    {
      "action": "save",
      "id": "r2",
      "expectedIds": [
        "r2"
      ]
    }
  ]
}
Reasoning
Intermediate expectations detect duplicate or reversed transitions that a final-only check can conceal.

Interview reasoning and next step

In interviews, contrast toggle with idempotent save using the same two calls.

In interviews, contrast toggle with idempotent save using the same two calls. Explain why retries matter and identify the postcondition you would read back. Avoid claiming that an idempotent HTTP method proves every implementation has correct retry semantics.

Next, use exploratory testing and a concise defect report to investigate behavior that a fixed table did not anticipate.

Optional stretch — after the core task works: Insert a failed request and define its effect.

Computer practice: download the companion lab at https://applydjinn.com/typorithm/qa-lab-20261105.zip. Read its README, install the pinned tools and run the accepted examples before attempting a starter. The mobile reader preserves your reading and self-check progress; the local lab runs browser tests separately.

Verify repeated actions and forbidden changes, not just a first successful click.