Skip to content

Extension Lab

Chapter lab: Extension Labch22-extension-lab.zip · 4 KB

This chapter works in a prepared project. Download it before the first task; it unpacks into its own folder.

Download ch22-extension-lab.zip

Or fetch and unpack it from a terminal in VS Code:

Terminal window
curl -fsSLO https://manykarim.github.io/robotframework-essentials-web/labs/ch22-extension-lab.zip && unzip -q ch22-extension-lab.zip && rm ch22-extension-lab.zip

Open the folder in VS Code with code rfe-ch22-extension-lab (or File → Open Folder…) and accept the recommended RobotCode and Python extensions. Then set it up:

Terminal window
uv sync
Terminal window
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Keep the environment active in every terminal where you run lesson commands.

Terminal window
poetry install --no-root

Python 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-ch22-extension-lab/
    • 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

Bring the two parts together into one small extension package — the shape a real internal Robot Framework extension takes:

  • a typed library the suite uses, documented with Libdoc, and
  • a listener that reports the run.

As you build, keep the design question in mind: a library adds keywords; a listener observes; a modifier reshapes what runs; a result visitor reshapes results; a parser reads new formats; a dynamic library is for keywords known only at run time. Reach for the simplest one that fits.

Robot Framework extension pointshub diagram. Reach for the simplest extension that fitsLibraryListenerModifierResult visitorParserDynamic libraryRobot Frameworkthe core runReach for the simplest extension that fits
Edit

Goal: create libraries/shipping.py with a typed keyword shipping_fee(weight) returning a base fee plus a per-kilogram charge, rounded to two decimals.

Here is the same shape over a different fee. Adapt it to a base of 4.99 plus 0.50 per kilogram of weight.

Worked example — adapt it, don’t copy it

def handling_fee(items: int) -> float:
return round(1.50 + 0.25 * items, 2)

Done when

libraries/shipping.py defines shipping_fee.

Hint 1

Annotate the parameter so Robot Framework converts it: def shipping_fee(weight: float) -> float:.

Hint 2

Return the rounded total: return round(4.99 + 0.50 * weight, 2).

Solution
def shipping_fee(weight: float) -> float:
return round(4.99 + 0.50 * weight, 2)
Edit

Goal: create ext/RunReport.py — a v3 listener that writes results/run_report.md as a table of the run’s tests.

Here is a status-count variant. Adapt it to collect rows and write a table.

Worked example — adapt it, don’t copy it

import pathlib
class StatusCount:
ROBOT_LISTENER_API_VERSION = 3
def __init__(self):
self.counts = {}
def end_test(self, data, result):
self.counts[result.status] = self.counts.get(result.status, 0) + 1
def close(self):
summary = ", ".join(f"{n} {status}" for status, n in self.counts.items())
pathlib.Path("results").mkdir(exist_ok=True)
pathlib.Path("results/counts.txt").write_text(summary + "\n", encoding="utf-8")

Done when

ext/RunReport.py is a v3 listener.

Hint 1

Collect (data.name, result.status) per test in end_test, keeping ROBOT_LISTENER_API_VERSION = 3.

Hint 2

In close(), write a | Test | Status | table to results/run_report.md.

Solution
import pathlib
class RunReport:
ROBOT_LISTENER_API_VERSION = 3
def __init__(self):
self.rows = []
def end_test(self, data, result):
self.rows.append((data.name, result.status))
def close(self):
lines = ["| Test | Status |", "| --- | --- |"]
lines += [f"| {name} | {status} |" for name, status in self.rows]
pathlib.Path("results").mkdir(exist_ok=True)
pathlib.Path("results/run_report.md").write_text("\n".join(lines) + "\n", encoding="utf-8")

Task 3 — Wire it together, run, and document

Section titled “Task 3 — Wire it together, run, and document”
Run

Create checks/shipping.robot that imports your library and uses the keyword:

*** Settings ***
Library ../libraries/shipping.py
*** Test Cases ***
Shipping Fee Is Charged
${fee}= Shipping Fee 3
Should Be Equal As Numbers ${fee} 6.49

Run it with your listener, then generate the library’s docs:

Terminal window
uv run robot --listener ext/RunReport.py checks/shipping.robot
uv run libdoc libraries/shipping.py results/shipping.html
Terminal window
robot --listener ext/RunReport.py checks/shipping.robot
libdoc libraries/shipping.py results/shipping.html
Terminal window
poetry run robot --listener ext/RunReport.py checks/shipping.robot
poetry run libdoc libraries/shipping.py results/shipping.html

Done when

the suite passes, and both results/run_report.md and results/shipping.html were generated.

Check your understanding

Quick recall. Your first answer stays in this browser; the quiz never blocks your progress.

  1. For keywords known only at run time (generated from data), you reach for…

    Show the answer

    Answer: a dynamic library — A dynamic library (get_keyword_names/run_keyword, or PythonLibCore) fits runtime-defined keywords.

  2. To reshape which tests run before execution, you use…

    Show the answer

    Answer: a pre-run modifier — A pre-run modifier edits the running model; a result visitor works after the run; a parser reads new formats.

Key terms

extension packageconcept
A library (+Libdoc) plus a listener — a small internal RF extension.
choosing a mechanismconcept
Library adds keywords · listener observes · modifier reshapes · visitor post-processes · parser reads formats.

All terms in the glossary →