A result visitor
Chapter lab: Shaping data and results
This lesson continues in the same lab as the lesson before it. If you don’t have it yet, download it here.
Download ch21-shaping-data-and-results.zip
Or fetch and unpack it from a terminal in VS Code:
curl -fsSLO https://manykarim.github.io/robotframework-essentials-web/labs/ch21-shaping-data-and-results.zip && unzip -q ch21-shaping-data-and-results.zip && rm ch21-shaping-data-and-results.zipInvoke-WebRequest https://manykarim.github.io/robotframework-essentials-web/labs/ch21-shaping-data-and-results.zip -OutFile ch21-shaping-data-and-results.zip; Expand-Archive ch21-shaping-data-and-results.zip -DestinationPath .; Remove-Item ch21-shaping-data-and-results.zipOpen the folder in VS Code with code rfe-ch21-shaping-data-and-results (or File → Open Folder…) and accept the recommended RobotCode and Python extensions. Then set it up:
uv syncpython3 -m venv .venvsource .venv/bin/activatepip install -r requirements.txtpython -m venv .venv.venv\Scripts\Activate.ps1pip install -r requirements.txtKeep the environment active in every terminal where you run lesson commands.
poetry install --no-rootPython 3.13 or newer is required; uv installs it when it is missing.
Open in browserNo setup? Try the lab right here: its files, an editor and a terminal, running in your browser.
What's inside
Directoryrfe-ch21-shaping-data-and-results/
Directory.vscode/
- extensions.json
Directorychecks/
- orders.robot
Directorydata/
- orders.csv
Directoryext/
- README.md
- .gitignore
- .python-version
- LAB.md
- pyproject.toml
- README.md
- requirements.txt
- uv.lock
A pre-run modifier shapes what runs. A result visitor shapes what you do
with the results — the output.xml a run produces. It is a ResultVisitor:
Robot Framework walks the finished suites and tests and calls your methods, so
you can build a custom report, compute metrics, or export data.
Because results live in output.xml, you can process them after the run,
with rebot, without re-running anything — reporting is decoupled from
execution.
Task 1 — Turn results into a markdown report
Section titled “Task 1 — Turn results into a markdown report”Goal: create ext/ResultReport.py — a ResultVisitor that collects each
finished test and, at the end of the top suite, writes results/rebot_report.md
as a markdown table.
Here is the same base class with a different job — recording only the failures. Adapt it to collect every test into a table.
Worked example — adapt it, don’t copy it
import pathlibfrom robot.api import ResultVisitor
class FailReport(ResultVisitor): def __init__(self): self.failed = []
def visit_test(self, test): if test.status == "FAIL": self.failed.append(test.name)
def end_suite(self, suite): if suite.parent is not None: return pathlib.Path("results").mkdir(exist_ok=True) pathlib.Path("results/failures.txt").write_text("\n".join(self.failed) + "\n", encoding="utf-8")Done when
ext/ResultReport.py defines a ResultVisitor.
Hint 1
Collect rows as you visit: def visit_test(self, test): appends
(test.name, test.status) to self.rows.
Hint 2
Write once, at the top suite. end_suite fires for every suite, so guard on the
root: if suite.parent is not None: return, then build the table and
write_text it to results/rebot_report.md.
Solution
import pathlibfrom robot.api import ResultVisitor
class ResultReport(ResultVisitor): def __init__(self): self.rows = []
def visit_test(self, test): self.rows.append((test.name, test.status))
def end_suite(self, suite): if suite.parent is not None: return lines = ["| Test | Status |", "| --- | --- |"] lines += [f"| {name} | {status} |" for name, status in self.rows] pathlib.Path("results").mkdir(exist_ok=True) pathlib.Path("results/rebot_report.md").write_text("\n".join(lines) + "\n", encoding="utf-8")Task 2 — Reprocess the saved results
Section titled “Task 2 — Reprocess the saved results”Run the suite once to produce output.xml, then build your report from it with
rebot — no tests re-run:
uv run robot --outputdir results checks/orders.robotuv run rebot --prerebotmodifier ext/ResultReport.py results/output.xmlrobot --outputdir results checks/orders.robotrebot --prerebotmodifier ext/ResultReport.py results/output.xmlpoetry run robot --outputdir results checks/orders.robotpoetry run rebot --prerebotmodifier ext/ResultReport.py results/output.xmlDone when
results/rebot_report.md is a table built from the saved results.
Check your understanding
Quick recall. Your first answer stays in this browser; the quiz never blocks your progress.
A result visitor works on…
A
ResultVisitorwalks the finished results, so reporting is decoupled from execution.Show the answer
Answer: the results in
output.xmlafter a run — AResultVisitorwalks the finished results, so reporting is decoupled from execution.How do you reprocess a saved run without re-running tests?
rebotreadsoutput.xmland applies your visitor — no tests execute.Show the answer
Answer:
rebot --prerebotmodifier Visitor.py output.xml—rebotreadsoutput.xmland applies your visitor — no tests execute.
Key terms
- ResultVisitorconcept
- Walk finished results; build reports or metrics from
output.xml. - rebotcommand
- Reprocess a saved
output.xmlinto new reports.