Chapter 10Lesson 01150–210 min

IF/ELSE, FOR, WHILE, TRY/EXCEPT, BREAK, CONTINUE, and Control Flow: Core Concepts and Mental Model

Build a mental model for Robot Framework native branching, iteration, loop control, and structured error recovery while separating expression evaluation from keyword execution and keeping business-facing tests readable.

Native control flowExpressionsStatus propagationBounded loopsRecovery

Learning objectives

  • Explain where IF/ELSE, FOR, WHILE, TRY/EXCEPT, BREAK, and CONTINUE sit in Robot Framework execution.
  • Distinguish Robot variable replacement from Python-expression evaluation and choose safer $variable access for strings and objects.
  • Explain how branch/loop/recovery choices affect keyword and test status rather than merely source-code appearance.
  • Use explicit loop limits and narrow error matching as production defaults.
  • Recognize when control logic belongs in a user keyword or Python library instead of a business-facing test case.

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. The practical problem: automation needs decisions without becoming a hidden program

Chapters 01–09 established suites, keywords, variables, standard libraries, and deterministic lifecycle ownership. Real automation still needs decisions: skip an item that is not actionable, iterate over synthetic inputs, poll a bounded local state, or recover from one expected failure while preserving all others.

Robot Framework now has native control structures for these cases. The design risk is not lack of power; it is putting so much branching and recovery logic in a test that the executable specification stops communicating business intent. This chapter therefore treats control flow as an observable, bounded orchestration tool, not a second general-purpose language.

Control flow changes execution paths, not ownership boundaries
flowchart TD
A[Test/task or user keyword] --> B{Control structure}
B -->|IF/ELSE| C[Chosen branch]
B -->|FOR/WHILE| D[Bounded iterations]
B -->|TRY| E[Keyword execution]
E -->|PASS| F[ELSE if present]
E -->|Matched FAIL| G[EXCEPT branch]
E -->|Unmatched FAIL| H[Test/keyword fails]
F --> I[FINALLY if present]
G --> I
H --> I
C --> J[Next statement / status]
D --> J
I --> J
K[BREAK / CONTINUE] -. only valid in active loop body .-> D

2. Define the objects before writing conditions

Object What it owns What control flow may change
Test/task Automation intent and final PASS/FAIL/SKIP result Which keywords execute and which failure becomes final
User keyword Reusable orchestration contract Local branches, loops, recovery, and returned values
Variable A value in a defined scope Read for conditions; possibly reassigned by executed branches
Expression Python-evaluated condition only Decision result, not external system state by itself
External state Files/process/browser/API/database/etc. Changes only when executed keywords mutate it
Result evidence output.xml/log/report hierarchy and messages Records branches, loop iterations, failures, and recovery

3. Native structure map

Structure Primary use Production guardrail
IF / ELSE IF / ELSE Choose one branch Keep condition understandable; extract complex policy
FOR Iterate known finite data Prefer finite input and meaningful loop variable names
WHILE Repeat while state changes Always set an explicit small iteration/time limit for course/production patterns
BREAK Exit current loop early Use only when the exit condition is obvious and evidenced
CONTINUE Skip remaining current iteration Do not silently discard unexplained failures
TRY / EXCEPT Handle an expected keyword failure Match the expected message narrowly; unmatched failures must propagate
ELSE in TRY Run only when TRY succeeds Useful for success-only post-processing
FINALLY Always execute local cleanup Do not replace suite/test teardown ownership indiscriminately

4. Expression evaluation: ${var} replacement and $var access are not the same

IF and WHILE conditions are evaluated as Python expressions. With ${name}, Robot first replaces the variable with its string representation and then evaluates the resulting expression. This is convenient for numbers but fragile for arbitrary strings, multiline values, quotes, and complex objects. The special $name syntax gives the evaluator the actual object.

*** Test Cases ***
Safer String Condition
    VAR    ${state}    ready
    IF    $state == "ready"
        Log    State is ready.
    END

Numeric Replacement Can Be Fine
    VAR    ${count}    ${3}
    IF    ${count} > 0
        Log    Positive count.
    END

Trust boundary. An expression is executable Python syntax. Do not concatenate untrusted external text into expressions or pass arbitrary user-controlled strings to Evaluate. Keep conditions authored by the suite and pass data through variables such as $value.

5. Status propagation is the real behavior

An IF whose condition is false is not a failure; its body simply does not execute. A normal FOR completes with PASS unless a keyword fails or a loop-control statement changes the path. A WHILE that hits its configured limit normally fails. A TRY failure becomes handled only when an EXCEPT branch matches and that handler itself succeeds.

Therefore “the log reached the next line” is not enough evidence. Inspect the error message, which branch matched, loop status, final test status, and surrounding lifecycle cleanup.

6. WHILE is bounded by default—but set a deliberate limit anyway

Robot Framework 7.4.2 has a built-in default of 10,000 WHILE iterations. That safety net is much too large to serve as a domain policy. A production test should set a meaningful count or time budget tied to the operation being modeled.

*** Keywords ***
Advance Until Ready
    [Arguments]    ${max_attempts}=5
    VAR    ${attempt}    ${0}
    VAR    ${state}      warming
    WHILE    $state != "ready"    limit=${max_attempts}
        ${attempt}=    Evaluate    $attempt + 1
        ${state}=      Simulate State    ${attempt}
        Log    attempt=${attempt}; state=${state}
    END
    RETURN    ${state}

7. BREAK and CONTINUE belong to the loop body

BREAK exits the current FOR or WHILE. CONTINUE skips the rest of the current iteration. Both may be nested inside IF or TRY, but they must still be syntactically in the loop body. Calling a helper keyword that itself contains BREAK is invalid; a helper should instead return a decision that the loop body acts on.

FOR    ${item}    IN    alpha    skip    stop    omega
    IF    $item == "skip"    CONTINUE
    IF    $item == "stop"    BREAK
    Log    processing=${item}
END

8. TRY/EXCEPT matches Robot error messages—not Python exception classes

Robot Framework error recovery deals primarily with keyword failure messages. Exact matching is the default. Pattern modes such as GLOB, REGEXP, and START are available when the variable portion of an expected message must be tolerated. Invalid syntax and failures that stop the whole execution cannot be caught with ordinary EXCEPT.

TRY
    Fail    transient: service warming
EXCEPT    transient:*    type=GLOB    AS    ${error}
    Log    Recovered expected failure: ${error}
ELSE
    Log    No failure occurred.
FINALLY
    Log    Local cleanup/evidence step.
END

9. Put control flow at the narrowest readable layer

Situation Good location Why
One business-visible choice Test/task or domain keyword The branch communicates intent
Iteration over test data User keyword or template chapter later Keeps case readable and loop reusable
Small bounded polling Named user keyword Centralizes limit/evidence contract
Complex policy graph or algorithm Python library Robot syntax should not become general application code
Expected operational failure mapping User keyword with narrow TRY/EXCEPT Preserves failure semantics behind a clear domain contract

10. Why this matters in DevOps

CI needs deterministic control flow. A green pipeline is trustworthy only if a branch cannot silently hide a failed assertion, a retry loop cannot run forever, and recovery is limited to known transient conditions. Bounded control flow also improves parallelism planning because each test has an understandable upper execution cost.

11. Knowledge check

Why is $state often safer than embedding ${state} directly in an IF expression?

What happens when a WHILE loop reaches its normal limit?

Can BREAK be placed inside a user keyword that is called from a FOR loop?

Does an EXCEPT without a message make all failures safe to ignore?

12. Summary and next step

Native control flow changes which Robot statements execute while lifecycle and external-state ownership remain separate. Conditions are Python expressions, loops must be bounded, BREAK/CONTINUE belong to the active loop body, and TRY/EXCEPT should recover only explicitly expected failures. Lesson 2 turns that model into a synthetic state-processing workflow with observable traces.

Next lesson

IF/ELSE, FOR, WHILE, TRY/EXCEPT, BREAK, CONTINUE, and Control Flow: Guided Hands-On Workflow

Continue with IF/ELSE, FOR, WHILE, TRY/EXCEPT, BREAK, CONTINUE, and Control Flow: Guided Hands-On Workflow. It builds directly on the state, evidence, and operating assumptions established here, so carry those constraints forward rather than treating the next page as an isolated topic.

Further reading

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.