Checkpoint Lab — IF/ELSE, FOR, WHILE, TRY/EXCEPT, BREAK, CONTINUE, and Control Flow
Implement a synthetic state machine using IF/FOR/WHILE/TRY, enforce hard limits, inject transient and permanent failures, prove only the intended failure is recovered, and refactor nested logic into named user keywords.
Learning objectives
- Model a small synthetic state machine with explicit branch, iteration, recovery, and terminal states.
- Enforce both iteration and semantic limits so the lab cannot run indefinitely.
- Inject transient and permanent failures and prove only the transient failure is recovered.
- Preserve branch/loop/error evidence in the Robot output directory.
- Refactor an over-nested implementation into user keywords with clear contracts and final status propagation.
Current compatibility baseline. Verified
2026-08-31: Robot Framework 7.4.2 is the current stable release;
7.5b1 is a pre-release and is not required in this chapter. Native
IF/ELSE arrived in Robot Framework 4.0;
WHILE, TRY/EXCEPT, BREAK,
CONTINUE, and inline IF are available in
modern Robot Framework 5.0+ syntax. Robot Framework 7.4.2 gives
WHILE a default 10,000-iteration limit; production
examples here set a smaller explicit limit.
1. Checkpoint charter and safety boundary
The entire state machine is synthetic. It writes only a trace file
under the selected Robot output directory. It does not call
networks, browsers, databases, SSH, containers, external processes,
or production systems. Failure injection uses Fail with
fake messages.
Success criterion. The checkpoint is not “everything passes.” One diagnostic run must fail with a permanent error. The goal is to prove that transient recovery is narrow and permanent failure remains visible.
2. State model and predictions
stateDiagram-v2 [*] --> queued queued --> warming warming --> warming: transient failure/retry warming --> ready: successful observation warming --> failed: permanent failure ready --> [*] failed --> [*]
| Input mode | Expected path | Expected final result |
|---|---|---|
| normal | queued → warming → ready | PASS |
| transient-once | queued → warming → transient caught → ready | PASS |
| permanent | queued → warming → permanent failure | FAIL; permanent message preserved |
| never-ready | queued → warming until hard limit | FAIL with convergence/limit message |
3. Refactored implementation
*** Settings ***
Library OperatingSystem
*** Variables ***
${TRACE} ${OUTPUT DIR}/state-machine-trace.txt
*** Test Cases ***
Normal State Machine
${final}= Run Synthetic State Machine normal max_steps=6
Should Be Equal ${final} ready
Transient Failure Is Recovered
${final}= Run Synthetic State Machine transient-once max_steps=6
Should Be Equal ${final} ready
Permanent Failure Is Not Recovered
[Tags] expected-diagnostic-fail
Run Synthetic State Machine permanent max_steps=6
Never Ready Is Bounded
[Tags] expected-diagnostic-fail
Run Synthetic State Machine never-ready max_steps=4
*** Keywords ***
Run Synthetic State Machine
[Arguments] ${mode} ${max_steps}=6
Create File ${TRACE} mode=${mode}\n
VAR ${state} queued
VAR ${step} ${0}
WHILE $state != "ready" limit=${max_steps}
${step}= Evaluate $step + 1
Trace step=${step};state=${state}
${state}= Advance One State ${state} ${mode} ${step}
IF $state == "failed"
Fail permanent: synthetic state machine failed at step ${step}
END
END
Trace final=${state};steps=${step}
RETURN ${state}
Advance One State
[Arguments] ${state} ${mode} ${step}
IF $state == "queued"
RETURN warming
END
IF $state != "warming"
Fail permanent: unknown state '${state}'
END
TRY
${next}= Observe Synthetic Warming State ${mode} ${step}
EXCEPT transient:* type=GLOB AS ${error}
Trace caught=${error}
RETURN warming
END
RETURN ${next}
Observe Synthetic Warming State
[Arguments] ${mode} ${step}
IF $mode == "transient-once" and $step == 2
Fail transient: synthetic dependency warming
ELSE IF $mode == "permanent" and $step == 2
Fail permanent: synthetic configuration invalid
ELSE IF $mode == "never-ready"
RETURN warming
ELSE
RETURN ready
END
Trace
[Arguments] ${message}
Append To File ${TRACE} ${message}\n ... encoding=UTF-8
4. Why the permanent failure is not caught
Advance One State catches only messages matching
transient:*. The permanent failure from
Observe Synthetic Warming State does not match and
therefore propagates through the user keyword to the test. This is
the central checkpoint proof.
5. Preflight
python --version
robot --version
robot --dryrun --outputdir evidence/dryrun suites/state_machine.robot
6. Run PASS and diagnostic FAIL slices separately
# PASS paths
robot --exclude expected-diagnostic-fail --outputdir evidence/pass suites/state_machine.robot
# Permanent failure only
robot --test "Permanent Failure Is Not Recovered" --outputdir evidence/permanent suites/state_machine.robot
# Never-ready limit failure only
robot --test "Never Ready Is Bounded" --outputdir evidence/limit suites/state_machine.robot
The first command should PASS. The latter two should exit non-zero. Preserve all three output directories.
7. Verify normal and transient traces
| Run | Trace evidence | Expected |
|---|---|---|
| normal | step=1 queued; step=2 warming; final=ready | No caught= line |
| transient-once | caught=transient:... then another warming iteration | Final ready and PASS |
8. Verify only intended failure is recovered
For Permanent Failure Is Not Recovered, inspect
log.html and output.xml. The error must
contain permanent: synthetic configuration invalid.
There must be no later final=ready trace. For
Never Ready Is Bounded, the final failure must identify
the WHILE limit rather than an infinite hang.
9. Refactoring exercise: recognize excessive nesting
Before the refactored implementation above, imagine all state
transitions, failure matching, and trace calls were embedded
directly in one test case. That would make the test describe
implementation mechanics instead of the requirement. The checkpoint
refactor creates three contracts:
Run Synthetic State Machine,
Advance One State, and
Observe Synthetic Warming State. Each name explains one
responsibility and each failure path remains visible in the log
hierarchy.
10. Add FOR/BREAK/CONTINUE evidence
Add a separate helper that scans candidate modes before starting the
machine. Skip disabled with CONTINUE and
stop on the first supported mode with BREAK. Keep those
statements in the FOR body:
Select First Supported Mode
[Arguments] @{modes}
VAR ${selected} ${None}
FOR ${mode} IN @{modes}
IF $mode == "disabled" CONTINUE
IF $mode in ["normal", "transient-once", "permanent", "never-ready"]
VAR ${selected} ${mode}
BREAK
END
END
IF $selected is None
Fail No supported mode supplied
END
RETURN ${selected}
11. Required evidence packet
- Robot and Python version output.
- Exact PASS, permanent-failure, and limit-failure commands.
-
state-machine-trace.txtfor the PASS/transient cases. -
output.xml,log.html, andreport.htmlfor all three run classes. - Permanent failure message showing it was not caught.
- WHILE-limit failure showing bounded non-convergence.
- Before/after note explaining why nested test logic was extracted into named keywords.
- Trace or log evidence for the FOR/BREAK/CONTINUE extension.
12. Production control-flow convention
| Concern | Convention |
|---|---|
| IF/ELSE | One readable policy decision; extract when nesting obscures intent |
| FOR | Finite known collection; BREAK/CONTINUE only in active loop body |
| WHILE | Explicit small count/time budget tied to the domain |
| TRY/EXCEPT | Catch only expected recoverable failure messages; preserve unmatched failures |
| Expressions | Static authored expression + $variable data for arbitrary strings/objects |
| Retries | Evidence per attempt; permanent failures exit immediately |
| Complexity | Move algorithmic logic to Python library; Robot stays orchestration/specification |
13. Knowledge check
What proves the transient handler is not hiding permanent defects?
The separate permanent diagnostic run fails with the original permanent message because it does not match transient:*.
Why are the diagnostic failure tests run separately?
To preserve attributable first-failure artifacts and avoid mixing the permanent and limit failures in one result tree.
What is the purpose of both max_steps and a WHILE limit?
They express the domain budget and provide a framework-enforced hard stop. In this simple implementation the same value drives both, making the bound explicit and auditable.
Why is the refactored test more maintainable than one giant state-machine test body?
The test communicates the business outcome while named keywords own transition/recovery mechanics, giving clearer logs and smaller contracts to review.
What concept follows in Chapter 11?
Data-driven testing, templates, embedded arguments, and variants—ways to represent multiple input cases without turning every variation into manual control flow.
14. Checkpoint complete
You can now build bounded native control flow that remains an executable specification: branches are explicit, loops have hard budgets, loop control stays in the active loop body, expression data is handled safely, and recovery is narrow enough that permanent failures still fail. Chapter 11 builds on this by separating data variation from procedural branching.
Further reading
- Robot Framework 7.4.2 User Guide — control structures — FOR, WHILE, BREAK/CONTINUE, IF/ELSE, TRY/EXCEPT, and readability guidance.
-
Robot Framework 7.4.2 User Guide — WHILE loops
— expression evaluation, limits,
on_limit, and output-size considerations. -
Robot Framework 7.4.2 User Guide — TRY/EXCEPT
— exact/pattern matching,
AS,ELSE,FINALLY, and uncatchable failures. -
Robot Framework 7.4.2 User Guide — evaluating expressions
—
${var}replacement versus$varaccess and evaluation namespace behavior. - Robot Framework 7.4.2 BuiltIn — legacy-compatible conditional/loop-control helpers and native-syntax recommendations.
- Robot Framework project site — current stable release stream.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.