Chapter 11Lesson 05260–350 min

Checkpoint Lab — Data-Driven Testing, Templates, Embedded Arguments, and Variants

Build a compact data-driven build-ID validation suite with boundary and negative variants, prove each report entry is diagnosable, deliberately add a redundant environment dimension, and reduce it with a documented risk rationale.

Checkpoint labBoundary variantsDiagnosable reportsMatrix reductionEvidence packet

Learning objectives

  • Create a deterministic local validation rule and represent it with separately named templated variants.
  • Include boundary, positive, and negative cases without using expected-error suppression.
  • Predict report/test identity and failure behavior before execution, then verify it independently.
  • Inject one wrong expectation and preserve the resulting first-failure packet.
  • Add and then remove a redundant matrix dimension using a documented causal/risk argument.

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. In 7.4.2, templates can use normal, named, and embedded arguments. A templated test containing multiple data rows executes all rows even when one fails or is skipped, then aggregates the test status. User-keyword argument type conversion is stable from Robot Framework 7.3.

1. Checkpoint charter and safety boundary

Validate synthetic build IDs only. The rule accepts exactly AA-000 style identifiers: two uppercase ASCII letters, one dash, and three digits. No external system is contacted. No files are mutated except Robot Framework result artifacts in the chosen output directories. Use only fake data.

Evidence-first rule. One run is intentionally made to fail by changing an expected classification. Do not delete or overwrite that result directory after repair.

2. Predict the state/result changes

Checkpoint variant flow
flowchart TD
A[Named build-ID variant] --> B[Test Template]
B --> C[Build ID value + expected class]
C --> D[Classifier keyword]
D --> E[ACCEPT / REJECT]
E --> F[Hard assertion]
F --> G[Per-test PASS / FAIL]
G --> H[results/<run>/output.xml + log.html + report.html]
Prediction Before run How to verify
Each named row becomes one test 6 named tests Report and output.xml test count
Negative data is not a Robot failure by itself REJECT rows can PASS Expected/actual classification match
Wrong expectation fails one named test Non-zero process exit Failure message + report row
Adding environment values does not change classifier output Environment is non-causal Same result across synthetic env labels

3. Implement the compact suite

*** Settings ***
Test Template    Build ID "${build_id}" should be ${expected}

*** Test Cases ***                  BUILD_ID    EXPECTED
Nominal valid                        AB-123      ACCEPT
Lower numeric boundary               AB-000      ACCEPT
Upper numeric boundary               ZZ-999      ACCEPT
Lowercase rejected                   ab-123      REJECT
Missing dash rejected                AB123       REJECT
Wrong digit count rejected           AB-12       REJECT

*** Keywords ***
Build ID "${build_id}" should be ${expected}
    ${actual}=    Classify Build ID    ${build_id}
    Should Be Equal    ${actual}    ${expected}
    ...    msg=build-id classification mismatch for ${build_id}

Classify Build ID
    [Arguments]    ${build_id}
    ${matches}=    Run Keyword And Return Status
    ...    Should Match Regexp    ${build_id}    ^[A-Z]{2}-[0-9]{3}$
    IF    $matches
        RETURN    ACCEPT
    END
    RETURN    REJECT

The classifier converts a pattern-match outcome into a domain classification. The test then performs the hard assertion. Thus a negative input such as ab-123 passes only when it is classified as REJECT.

4. Preflight

python --version
python -m robot --version
python -m robot --dryrun --outputdir evidence/dryrun suites/build_ids.robot

Record the exact interpreter/framework versions and the source revision/checksum in your evidence notes.

5. Run the clean baseline

python -m robot --outputdir evidence/baseline suites/build_ids.robot

Expected: six tests, all PASS, zero exit status. Open report.html and verify that every business variant is a separately named test rather than one anonymous grouped matrix.

6. Inject one reversible failure

Change only this row:

Lowercase rejected                   ab-123      ACCEPT
python -m robot --outputdir evidence/injected-failure suites/build_ids.robot

Expected: the command exits non-zero and the test Lowercase rejected fails. The failure message must include ab-123. Restore the row to REJECT, but keep evidence/injected-failure unchanged.

7. Verify the repair independently

python -m robot --test "Lowercase rejected" --outputdir evidence/repaired suites/build_ids.robot

Expected: one selected test and PASS. Compare the injected and repaired logs rather than overwriting the original failure.

8. Deliberately create a redundant environment dimension

The classifier never reads an environment. To demonstrate matrix explosion, imagine multiplying each of the six rows by dev, stage, and prod. That produces 18 tests with no new classifier code path.

Original risk case dev stage prod New behavioral information?
Nominal valid same same same No
Lowercase rejected same same same No
Missing dash rejected same same same No

If you want to demonstrate the redundancy in source, temporarily add an ${environment} argument that is only logged and never used by the classifier. Run it once, then remove the dimension. The final project should not keep a non-causal 3× multiplier.

9. Write the risk-based reduction rationale

Your final note should say:

The build-ID classifier is a pure local rule whose inputs are only the build-ID characters. Deployment environment is not read by the classifier and does not alter its output. Therefore environment is not multiplied across syntax-boundary variants. Environment wiring belongs in a separate integration contract if/when the system behavior actually differs by environment.

This is stronger than “we reduced tests to save time” because it identifies the causal boundary.

10. Required evidence packet

  • Python and Robot Framework version output.
  • Exact --dryrun, baseline, injected-failure, and repaired commands.
  • The final six-row template source.
  • Baseline output.xml, log.html, and report.html.
  • Injected-failure artifacts showing the named row and concrete ab-123 context.
  • Repaired single-test artifacts.
  • A table or note showing 6 × 3 = 18 redundant environment combinations.
  • The written causal/risk reduction rationale.

11. Verification checklist

  • Exactly six final named variants exist.
  • Every REJECT row passes because classification equals expected output, not because an error was swallowed.
  • No real credentials, PII, production URLs, or generated random values are present.
  • The injected-failure directory remains intact after repair.
  • The final suite does not include the redundant environment multiplier.
  • report.html gives each final variant an independently understandable name.
  • The suite uses Robot Framework core/BuiltIn only.

12. Cleanup / rollback

No system-under-test state exists to clean. If you want to remove lab evidence, first archive the evidence packet required by your learning/CI policy, then delete only the local evidence/ directory you created for this lab. Do not use recursive deletion commands against an unverified path.

13. What this adds to a production operating model

Chapter 11 adds a governance rule for variation: one template contract, explicit risk-significant data, stable variant names, deterministic provenance, and result identity that matches the rerun/ownership need. It also establishes that matrix size is an architecture decision—not a badge of coverage.

14. Knowledge check

Why does “Lowercase rejected → REJECT” pass without hiding a failure?

What evidence proves the injected failure is attributable?

Why is environment removed from the final matrix?

If each environment later changes the rule, what should happen?

What does Chapter 12 add next?

15. Checkpoint complete

You can now create compact data-driven suites without sacrificing diagnosability: variant names remain meaningful, negative inputs use explicit output contracts, failures preserve row context, and redundant dimensions are removed with a causal risk rationale. Chapter 12 moves from variant design to reusable project organization and import boundaries.

Next lesson

Resource Files, Variable Files, Library Imports, and Project Layout: Core Concepts and Mental Model

Continue with Resource Files, Variable Files, Library Imports, and Project Layout: 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.