Chapter 09Lesson 02190–260 min

Setups, Teardowns, Tags, Timeouts, and Suite Lifecycle: Guided Hands-On Workflow

Instrument a disposable local suite so lifecycle order, tag selection, failure propagation, timeout behavior, filesystem state, and teardown guarantees are observable rather than assumed.

Hands-onLifecycle markersPASS/FAIL/TIMEOUTTag selectionCleanup

Learning objectives

  • Create a guarded suite workspace and per-test files with suite/test setup and teardown.
  • Record lifecycle markers in Robot output evidence so cleanup does not erase the proof.
  • Run separate PASS, FAIL, and TIMEOUT slices and predict exact lifecycle order before execution.
  • Use static and runtime tags and compare include, skip, and exclude semantics.
  • Verify that cleanup removes only test-owned state while output.xml/log/report remain available.

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 here. Suite, test/task, and user-keyword setups/teardowns are supported; user-keyword [Setup] was added in Robot Framework 7.0. Test Tags is the modern suite-level tag setting, while Force Tags/Default Tags are deprecated. Test timeouts and user-keyword timeouts have different cleanup behavior, which this chapter treats explicitly.

1. Scenario: an observable lifecycle laboratory

The lab uses only Robot Framework core, BuiltIn, and the standard OperatingSystem library. Each test gets a unique file under a guarded scratch directory. A lifecycle marker is written under ${OUTPUT DIR}, which survives scratch cleanup and therefore becomes independent evidence.

Failure injection is deliberate. One test calls Fail; another uses Sleep only to trigger a deterministic timeout. This is not a synchronization pattern. Run the failure slices into separate output directories and preserve them before repairing anything.

2. Project layout

rf09-lifecycle-lab/
├── suites/
│   └── lifecycle.robot
└── evidence/

3. Complete instrumented suite

*** Settings ***
Library           OperatingSystem
Suite Setup       Begin Suite Lifecycle
Suite Teardown    End Suite Lifecycle
Test Setup        Begin Test Lifecycle
Test Teardown     End Test Lifecycle
Test Tags         rf09    lifecycle

*** Variables ***
${RUN_ID}          manual-001
${SCRATCH}         ${TEMPDIR}/rf09-lifecycle-${RUN_ID}
${MARKERS}         ${OUTPUT DIR}/lifecycle-markers.txt
${SUITE_OWNS_ROOT}    ${False}

*** Test Cases ***
Passing Path
    [Tags]    demo-pass
    Append Marker    ${TEST NAME}|body-start
    File Should Exist    ${TEST_FILE}
    Set Tags    runtime-observed
    Append Marker    ${TEST NAME}|body-end

Failing Path
    [Tags]    demo-fail
    Append Marker    ${TEST NAME}|body-start
    File Should Exist    ${TEST_FILE}
    Fail    intentional-body-failure
    Append Marker    ${TEST NAME}|unreachable

Timed Out Path
    [Tags]    demo-timeout
    [Timeout]    300 milliseconds
    Append Marker    ${TEST NAME}|body-start
    Sleep    2 seconds
    Append Marker    ${TEST NAME}|unreachable

*** Keywords ***
Begin Suite Lifecycle
    Guard Scratch
    Directory Should Not Exist    ${SCRATCH}
    Create Directory    ${SCRATCH}
    Set Suite Variable    ${SUITE_OWNS_ROOT}    ${True}
    Create File    ${MARKERS}    suite-setup\n
Begin Test Lifecycle
    ${safe}=       Evaluate    re.sub(r'[^A-Za-z0-9_.-]+', '_', $TEST_NAME)    modules=re
    ${test_file}=  Join Path    ${SCRATCH}    ${safe}.txt
    Set Test Variable    ${TEST_FILE}    ${test_file}
    Create File    ${TEST_FILE}    owned-by=${TEST NAME}\n
    Append Marker    ${TEST NAME}|test-setup

End Test Lifecycle
    Append Marker    ${TEST NAME}|test-teardown-start
    Run Keyword And Ignore Error    Remove File    ${TEST_FILE}
    File Should Not Exist    ${TEST_FILE}
    Append Marker    ${TEST NAME}|test-teardown-end

End Suite Lifecycle
    Append Marker    suite-teardown-start
    IF    ${SUITE_OWNS_ROOT}
        Guard Scratch
        Remove Directory    ${SCRATCH}    recursive=True
        Directory Should Not Exist    ${SCRATCH}
    ELSE
        Log    Scratch root was not created by this suite; leaving it untouched.
    END
    Append Marker    suite-teardown-end

Guard Scratch
    ${candidate}=    Normalize Path    ${SCRATCH}    case_normalize=True
    ${temp}=         Normalize Path    ${TEMPDIR}    case_normalize=True
    Should Start With    ${candidate}    ${temp}${/}
    Should Contain       ${candidate}    rf09-lifecycle-

Append Marker
    [Arguments]    ${text}
    Append To File    ${MARKERS}    ${text}\n    ...    encoding=UTF-8

4. Why one ignored cleanup call is acceptable here—and what it does not mean

Run Keyword And Ignore Error Remove File is used only for an idempotent cleanup attempt: the next line still asserts that the file is absent, so a real cleanup failure remains visible. We are not converting a business failure into green status. In production, a dedicated cleanup keyword or TRY/EXCEPT can express the same intent more clearly; Chapter 10 develops native control flow in depth.

5. Preflight

python --version
robot --version
robot --dryrun --outputdir evidence/dryrun suites/lifecycle.robot

The dry run proves parsing, imports, keyword resolution, tags, and timeout syntax. It does not create files, run Sleep, or exercise teardown behavior.

6. Run the PASS slice first

robot --include demo-pass --variable RUN_ID:pass-001 --outputdir evidence/pass suites/lifecycle.robot

Expected status: PASS. Open evidence/pass/lifecycle-markers.txt. The expected sequence is suite-setup → test-setup → body-start → body-end → test-teardown-start → test-teardown-end → suite-teardown-start → suite-teardown-end. Confirm runtime-observed appears in the test’s final tags in the report/log.

7. Run the intentional failure slice

robot --include demo-fail --variable RUN_ID:fail-001 --outputdir evidence/fail suites/lifecycle.robot
# Expected command exit status: non-zero because the test fails.

The unreachable marker must be absent, but both test teardown markers and suite teardown markers must exist. The final error should still contain intentional-body-failure. Confirm the scratch directory is absent after execution.

8. Run the timeout slice

robot --include demo-timeout --variable RUN_ID:timeout-001 --outputdir evidence/timeout suites/lifecycle.robot
# Expected: FAIL due to the 300 ms test timeout.

The 2-second Sleep is deliberately longer than the 300 ms test timeout. Robot stops the current body keyword, marks the test failed, then runs test teardown. The teardown is not cut short by the test timeout. This demonstrates timeout semantics only; never use Sleep as a real readiness strategy.

9. Compare include, skip, and exclude

# Include only tests with demo-pass.
robot --include demo-pass --outputdir evidence/include suites/lifecycle.robot

# Keep demo-timeout visible as SKIP instead of executing it.
robot --skip demo-timeout --outputdir evidence/skip suites/lifecycle.robot

# Omit demo-timeout from the execution tree entirely.
robot --exclude demo-timeout --outputdir evidence/exclude suites/lifecycle.robot

--skip and --exclude are observably different. A skipped test appears in logs/reports with SKIP status; an excluded test is not part of the execution result. Neither option creates isolation—the per-test setup still owns that job for tests that do execute.

10. Evidence checklist

Evidence PASS run FAIL run TIMEOUT run
Body end marker Present Absent after Fail Absent after timeout
Test teardown markers Present Present Present
Suite teardown markers Present Present Present
Scratch directory after run Absent Absent Absent
Final status PASS FAIL FAIL
Primary message No failure intentional-body-failure test timeout message

11. Small challenge

Add a fourth test tagged override whose [Setup] uses a different keyword and whose [Teardown] is explicitly empty. Predict which default lifecycle hooks disappear, then verify from the marker file. Afterward restore the default teardown; disabling cleanup is a teaching experiment, not a production recommendation.

12. Knowledge check

Why are lifecycle markers stored under ${OUTPUT DIR} instead of the scratch workspace?

Why is Sleep acceptable in the timeout case here?

Why does the failing test still clean its file?

What is the observable difference between --skip and --exclude?

13. Summary and next step

You have observed lifecycle order instead of memorizing it. PASS, FAIL, and TIMEOUT runs prove that teardown ownership is independent from body success, while tag selection changes execution membership rather than state isolation. Lesson 3 turns these observations into design decisions for suite versus test fixtures, timeouts, dynamic tags, and failure-preserving cleanup.

Next lesson

Setups, Teardowns, Tags, Timeouts, and Suite Lifecycle: Configuration, Design Patterns, and Trade-Offs

Continue with Setups, Teardowns, Tags, Timeouts, and Suite Lifecycle: 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.