Chapter 05Lesson 05190–250 min

Checkpoint Lab — Keywords, Arguments, Return Values, and Reusable Abstractions

Refactor a flat synthetic suite into layered domain and utility keywords, prove behavior equivalence, inject a low-level failure, and leave an evidence packet showing that the high-level log still exposes the failing contract.

Checkpoint labDomain + utility layersBehavior equivalenceFailure injectionEvidence packet

Checkpoint objectives

  • Start from a known flat suite and record baseline PASS/FAIL behavior plus low-level call evidence.
  • Refactor the same behavior into high-level domain keywords and low-level utility keywords with explicit arguments and return values.
  • Prove the refactor does not change the intended outputs or test outcomes.
  • Inject one low-level failure without swallowing it and diagnose it from the high-level call hierarchy.
  • Produce a reusable keyword-interface convention and evidence packet suitable for code review and CI handoff.

Checkpoint safety boundary. Robot Framework 7.4.2, Python 3.12.x, BuiltIn + standard String only. All source, result artifacts, and failure injection stay inside a disposable rf-keyword-checkpoint directory. No external services, credentials, production data, paid tools, Pabot workers, containers, or CI runners are required.

1. Scenario: synthetic candidate labels with positive and negative validation

A release-quality team has a flat suite that builds synthetic candidate labels. It currently duplicates normalization and label construction. Your job is to refactor it into a readable domain layer while retaining enough nested technical evidence for diagnosis.

The checkpoint has two normal cases and one deliberately injected failing case. The failure must remain a real FAIL; do not turn it into data or retry it away.

2. Preflight and disposable project tree

python --version
python -m robot --version

mkdir rf-keyword-checkpoint
cd rf-keyword-checkpoint

Create this local structure:

rf-keyword-checkpoint/
├── flat.robot
├── refactored.robot
├── broken.robot
├── evidence/
│   ├── flat/
│   ├── refactored/
│   └── broken/
└── decisions/
    └── keyword-convention.md

Before writing code, record the absolute checkpoint path and confirm it is a disposable training directory. This is the only mutable filesystem scope.

3. Prediction sheet: state and evidence before execution

Write these predictions in decisions/predictions.md before running anything:

Question Prediction to record
What changes during the flat run? Only local Robot variables and result artifacts; no external state.
What should stay identical after refactor? Final labels, assertion outcomes, test names, and suite intent.
What should change after refactor? Call hierarchy: domain/utility user-keyword nodes appear in log.html.
Who owns raw candidate/component inputs? The test/caller.
Who owns normalized intermediate values? The utility/domain keyword call frame unless explicitly returned.
Who owns final label output? The caller after RETURN.
What should happen after injected low-level mismatch? Nested assertion fails; domain keyword and test fail; first-failure log remains visible.

4. Build and run the flat baseline

Create flat.robot:

*** Settings ***
Library    String

*** Test Cases ***
API Candidate Label
    ${raw}=    Set Variable    API Candidate 42
    ${lower}=    Convert To Lower Case    ${raw}
    ${slug}=    Replace String    ${lower}    ${SPACE}    -
    ${label}=    Catenate    SEPARATOR=:    qa    api    ${slug}
    Should Be Equal    ${label}    qa:api:api-candidate-42

UI Candidate Label
    ${raw}=    Set Variable    UI Candidate 42
    ${lower}=    Convert To Lower Case    ${raw}
    ${slug}=    Replace String    ${lower}    ${SPACE}    -
    ${label}=    Catenate    SEPARATOR=:    qa    ui    ${slug}
    Should Be Equal    ${label}    qa:ui:ui-candidate-42
python -m robot --outputdir evidence/flat flat.robot

Expected: two PASS tests. Preserve evidence/flat/output.xml, log.html, and report.html.

5. Refactor into domain and utility keyword layers

Create refactored.robot. The domain keyword expresses scenario intent; the utility keyword owns technical normalization. The caller still owns scenario inputs and final assertion.

*** Settings ***
Library    String

*** Test Cases ***
API Candidate Label
    ${label}=    Build Candidate Label    api    API Candidate 42    environment=qa
    Should Be Equal    ${label}    qa:api:api-candidate-42

UI Candidate Label
    ${label}=    Build Candidate Label    ui    UI Candidate 42    environment=qa
    Should Be Equal    ${label}    qa:ui:ui-candidate-42

*** Keywords ***
Build Candidate Label
    [Arguments]    ${component}    ${raw_candidate}    @{}    ${environment}=local
    ${slug}=    Normalize Candidate Id    ${raw_candidate}
    ${label}=    Catenate    SEPARATOR=:    ${environment}    ${component}    ${slug}
    RETURN    ${label}

Normalize Candidate Id
    [Arguments]    ${raw_candidate}
    ${lower}=    Convert To Lower Case    ${raw_candidate}
    ${slug}=    Replace String    ${lower}    ${SPACE}    -
    RETURN    ${slug}
python -m robot --outputdir evidence/refactored refactored.robot

Expected: the same two PASS outcomes and the same final labels. The new log should show Build Candidate Label → Normalize Candidate Id → String-library calls.

6. Prove behavior equivalence instead of assuming it

Complete this evidence table from the two runs:

Observation Flat Refactored Expected conclusion
API final label qa:api:api-candidate-42 same Behavior preserved.
UI final label qa:ui:ui-candidate-42 same Behavior preserved.
Test status PASS/PASS PASS/PASS No semantic regression.
Test names same same Suite identity preserved.
Low-level String calls Direct under test Nested under utilities Implementation is reusable but still visible.
Data flow Copied intermediates Explicit args + return Ownership improved.

If any final output/status changes, stop and fix the refactor before injecting a failure.

7. Capture keyword signature evidence

python -m robot.libdoc refactored.robot list
python -m robot.libdoc refactored.robot show "Build Candidate Label"
python -m robot.libdoc refactored.robot evidence/refactored-keywords.html

Verify that component and raw_candidate are normal required arguments and environment is named-only with default local. Record the output or screenshot in the evidence packet.

8. Inject a low-level failure without hiding it

Copy the refactored file to broken.robot. Add one deliberate assertion inside Normalize Candidate Id that requires the normalized slug to begin with api-. This makes the UI case fail at the utility level while the API case still passes.

Normalize Candidate Id
    [Arguments]    ${raw_candidate}
    ${lower}=    Convert To Lower Case    ${raw_candidate}
    ${slug}=    Replace String    ${lower}    ${SPACE}    -
    Should Start With    ${slug}    api-
    RETURN    ${slug}

Should Start With belongs to the imported String library. Do not catch or ignore its failure.

python -m robot --outputdir evidence/broken broken.robot

Expected: API Candidate Label PASS, UI Candidate Label FAIL. The command should return a non-zero exit status because at least one test failed.

9. Diagnose from the high-level log down to the failing contract

Open evidence/broken/log.html and follow the failing tree:

  1. UI Candidate Label — scenario intent failed.
  2. Build Candidate Label — domain capability failed while delegating normalization.
  3. Normalize Candidate Id — utility contract failed.
  4. String.Should Start With — the concrete assertion explains that ui-candidate-42 does not start with api-.

This is the desired diagnostic property: abstraction improved readability without erasing the primitive failure.

Do not “repair” the checkpoint by adding Run Keyword And Ignore Error, a retry, or a generic default return. The injected mismatch is evidence that the utility contract is wrong for the UI caller.

10. Repair the contract, not the symptom

The low-level assertion encoded an API-only rule inside a generic normalization utility. The correct repair is to remove that assertion from Normalize Candidate Id and, if the API scenario truly requires an API prefix, assert that requirement at the domain/test layer that owns it.

Re-run the repaired suite into evidence/repaired if you want a third comparison. Preserve evidence/broken unchanged.

11. Write the keyword-interface convention

Create decisions/keyword-convention.md containing at least these rules:

  1. High-level keywords express domain/operational intent; utilities express technical transformations.
  2. Callers own scenario choices; signatures expose those choices explicitly.
  3. Use defaults only for stable, safe project policy.
  4. Use named-only arguments for options whose unlabeled positional form would be ambiguous.
  5. Use RETURN for normal data flow; avoid suite/global mutation as helper output.
  6. Keep return shapes stable and documented.
  7. Do not hide failures; preserve nested context in logs.
  8. Qualify or rename ambiguous keyword owners; do not rely on guesswork.
  9. Wrap libraries only when the wrapper adds project policy/meaning.
  10. Keyword names must reveal meaningful side effects.

12. Assemble the checkpoint evidence packet

  • Python and Robot Framework version output;
  • flat.robot, refactored.robot, and broken.robot;
  • prediction sheet;
  • flat/refactored/broken execution commands and exit statuses;
  • three sets of output.xml/log.html/report.html;
  • Libdoc signature evidence;
  • behavior-equivalence table;
  • failure-path notes showing test → domain → utility → String assertion;
  • keyword-convention.md.

Everything is synthetic, but inspect artifacts before sharing them. In real projects, arguments, messages, screenshots, and result XML may contain sensitive data.

13. Verification checklist

  • Exactly two baseline tests PASS in both flat and refactored runs.
  • Final labels are byte-for-byte equivalent across the baseline/refactored runs.
  • The refactored log contains both domain and utility user-keyword nodes.
  • No suite/global variable is used for ordinary data flow.
  • Libdoc shows the intended required/named-only/default signature.
  • The injected UI failure remains FAIL and causes a non-zero Robot exit status.
  • The broken log exposes the failing String assertion under the correct higher-level call chain.
  • The broken result artifacts are preserved after repair.
  • No real secrets, production URLs, personal data, external accounts, or uncontrolled systems were used.

14. Cleanup and rollback

The lab creates only the local checkpoint directory and result artifacts. If you keep it, it becomes useful input for Chapter 06 variable-scope discussion. If deleting it, move to its parent directory, print/inspect the target, and remove only rf-keyword-checkpoint. There is no external rollback because no external service was mutated.

15. Knowledge check

Why is matching final output/status across flat and refactored runs stronger evidence than saying the code “looks equivalent”?

Why did the injected UI failure belong outside the generic normalization utility?

What evidence proves that abstraction did not destroy diagnostics?

Why is environment named-only in the checkpoint design?

What should Chapter 06 add to this operating model?

16. Production operating model and bridge to Chapter 06

Chapter 05 adds a reusable-interface layer to the Robot Framework operating model: clear keyword ownership, deliberate resolution, explicit argument contracts, stable return shapes, local data flow, honest side-effect names, shallow meaningful composition, and failure evidence that remains intact through abstraction.

Chapter 06 now examines the data carried through those interfaces: scalar, list, dictionary, environment, and dynamic variables; scope/lifetime; precedence; and the difference between explicit local values and shared mutable state.

Next lesson

Variables: Scalar, List, Dictionary, Environment, and Dynamic Values: Core Concepts and Mental Model

Continue with Variables: Scalar, List, Dictionary, Environment, and Dynamic Values: Core Concepts and Mental Model. 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.