Chapter 13Lesson 02240–330 min

Assertions, Error Handling, Expected Failures, and Recovery Patterns: Guided Hands-On Workflow

Build a local failure-semantics laboratory that exercises precise assertions, expected errors, narrow recovery, continuable failure, skip, bounded retry, and false-green detection while preserving first-failure evidence.

Hands-onExpected errorsRecoveryFirst failureFalse green

Learning objectives

  • Write precise assertions and inspect their error messages in output artifacts.
  • Implement one negative test with an exact expected-error contract.
  • Recover one known technical condition with narrow TRY/EXCEPT and let an unknown error propagate.
  • Compare hard failure, continuable failure, status-returning helpers, SKIP, and bounded retry.
  • Preserve first-failure evidence before running a retry or repair.

Current compatibility baseline. Verified 2026-08-31: Robot Framework 7.4.2 is the current stable release and requires Python 3.8+; 7.5b1 is a pre-release and is not required in this chapter. Native TRY/EXCEPT is the preferred modern error-handling surface. EXCEPT uses exact matching by default and supports type=GLOB, REGEXP, START, or LITERAL. BuiltIn Run Keyword And Expect Error defaults to glob matching. Continuable failures continue execution but still make the test fail. Skip/Skip If produce SKIP, and Wait Until Keyword Succeeds is a bounded retry helper for normal failures—not syntax errors, timeouts, or fatal execution-stopping errors.

1. Disposable scenario: validate synthetic release records

Create a fresh directory named rf13-failure-lab. The suite operates only on literal strings, numbers, and Robot variables. There is no browser, network, database, SSH target, process manipulation, credential, or production state. Generated evidence goes under results/.

2. Preflight and evidence directory

python --version
python -m robot --version
mkdir -p results

PowerShell equivalent for the directory is New-Item -ItemType Directory -Force results. Confirm Robot Framework 7.4.2 before comparing the documented messages/matching behavior. Do not delete an existing results directory: use unique subdirectories so the first failure survives.

3. Start with a small assertion suite

*** Settings ***
Documentation    Synthetic failure-semantics laboratory.

*** Keywords ***
Validate Release State
    [Arguments]    ${state}
    Should Be Equal    ${state}    ready    msg=Release state must be exactly 'ready'

Reject Negative Amount
    [Arguments]    ${amount}
    IF    $amount < 0
        Fail    amount must be non-negative
    END

*** Test Cases ***
Precise assertion passes
    Validate Release State    ready

Precise assertion fails
    Validate Release State    pending

Run this once into results/assertions. The second test should FAIL with the custom contract message and actual/expected evidence from the assertion. Keep that output directory; it is your baseline failure artifact.

4. Preserve first-failure artifacts explicitly

python -m robot --outputdir results/assertions lab.robot
# Keep results/assertions/output.xml, log.html, report.html unchanged.

A repair should go to a new output directory. Overwriting the original result removes the evidence needed to prove whether the failure mode changed.

5. Add an exact expected-error negative case

*** Test Cases ***
Negative amount is rejected for the intended reason
    ${error}=    Run Keyword And Expect Error
    ...    EQUALS:amount must be non-negative
    ...    Reject Negative Amount    ${-5}
    Should Be Equal    ${error}    amount must be non-negative

This test is green only when the intended error occurs. Change the expected text by one character and it must fail. That controlled break proves the negative test is not merely suppressing an error.

6. Recover one known condition and expose unknown failures

*** Keywords ***
Read Synthetic Cache
    [Arguments]    ${mode}
    IF    $mode == 'cold'
        Fail    cache-not-ready: node-a
    ELSE IF    $mode == 'broken'
        Fail    checksum-corrupt: node-a
    END
    RETURN    cached-value

Read With Narrow Recovery
    [Arguments]    ${mode}
    TRY
        ${value}=    Read Synthetic Cache    ${mode}
    EXCEPT    cache-not-ready:    type=START    AS    ${error}
        Log    Expected transient condition: ${error}
        ${value}=    Set Variable    fallback-value
    END
    RETURN    ${value}

*** Test Cases ***
Known condition is recovered
    ${value}=    Read With Narrow Recovery    cold
    Should Be Equal    ${value}    fallback-value

Unknown condition still fails
    Read With Narrow Recovery    broken

The first case can PASS because recovery is explicitly part of the keyword contract. The second must FAIL because checksum-corrupt does not match the START pattern. If both pass, the handler is too broad.

7. Compare hard failure with continuable failure

*** Test Cases ***
Hard failure stops normal body
    Should Be Equal    alpha    beta
    Log    This line is not executed

Continuable failure collects later evidence
    Run Keyword And Continue On Failure    Should Be Equal    alpha    beta
    Log    This line executes
    Log    But final test status remains FAIL

Run them separately or inspect their keyword trees in log.html. The distinction is flow, not final truth.

8. Intentionally create and repair a false green

*** Test Cases ***
False green — intentionally broken design
    ${ok}=    Run Keyword And Return Status    Should Be Equal    expected    actual
    Log    Validation status was ${ok}
    # No assertion on ${ok}: the test itself PASSes. This is the defect.

Repaired status contract
    ${ok}=    Run Keyword And Return Status    Should Be Equal    expected    actual
    Should Be True    ${ok}    msg=Validation result must be true

The first test demonstrates why a status-returning helper is not an assertion. It converts a normal failure into a Boolean. If the Boolean is unused, CI can see PASS. The repaired test turns the Boolean back into a required contract and correctly FAILs. In many real cases the clearer repair is to remove the wrapper and let the original assertion fail directly.

9. Add a legitimate skip without calling it success

*** Test Cases ***
Capability intentionally unavailable
    Skip    Synthetic capability is not provisioned in this local profile.
    Fail    This is never executed.

The result is SKIP. A test teardown, if present, still runs. Use a concrete reason so the report explains why no verification occurred.

10. Preserve a direct first failure before the bounded retry experiment

*** Keywords ***
Reset Attempt Counter
    VAR    ${ATTEMPT}    ${0}    scope=TEST

Eventually Ready
    ${next}=    Evaluate    $ATTEMPT + 1
    VAR    ${ATTEMPT}    ${next}    scope=TEST
    Log    attempt=${ATTEMPT}
    IF    $ATTEMPT < 3
        Fail    synthetic resource not ready yet
    END
    RETURN    ready

*** Test Cases ***
Bounded transient retry
    Reset Attempt Counter
    ${value}=    Wait Until Keyword Succeeds    3x    0s    Eventually Ready
    Should Be Equal    ${value}    ready
    Should Be Equal As Integers    ${ATTEMPT}    3

Before enabling the wrapper, run Eventually Ready once in a separate test/output directory and preserve the failure. Then run the bounded retry case. The three attempts should be visible in the log. This is deterministic teaching data—not permission to wrap flaky browser/API checks in blanket retries.

11. Expected status matrix

Scenario Expected final status Evidence to inspect
Precise assertion fails FAIL Assertion message + failed keyword in log
Exact negative error occurs PASS Expected match and returned message
Known TRY recovery PASS Matched EXCEPT branch + fallback verification
Unknown TRY error FAIL Unmatched error remains original failure
Continuable failure FAIL Later logs exist plus recorded failure
Skip SKIP Skip reason; no body after Skip
Bounded retry eventually succeeds PASS Attempt trace plus final value
Ignored Boolean false green PASS — intentionally wrong Wrapped assertion is FAIL internally but test has no required assertion
Repaired Boolean contract FAIL Should Be True exposes false status

12. Challenge: choose the correct layer

A deployment-readiness test receives maintenance-window, checksum-corrupt, or ready. Only maintenance-window is an approved reason to skip; checksum-corrupt must fail; ready must pass. Decide whether you need IF, TRY/EXCEPT, Skip, or an expected-error assertion—and justify the choice from final status semantics. Do not use a broad EXCEPT or retry.

13. Knowledge check

Why save results/assertions before changing the suite?

What should happen when Read With Narrow Recovery receives broken?

Why is the intentionally ignored ${ok} a false green?

When is a bounded retry defensible?

14. Summary and next step

You have now exercised every major status contract using synthetic data and concrete result evidence. Lesson 3 compares the design choices behind these mechanisms so teams can encode consistent policies instead of choosing wrappers ad hoc.

Next lesson

Assertions, Error Handling, Expected Failures, and Recovery Patterns: Configuration, Design Patterns, and Trade-Offs

Continue with Assertions, Error Handling, Expected Failures, and Recovery Patterns: Configuration, Design Patterns, and Trade-Offs. 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.