A class-based library with state
Chapter lab: Custom libraries
This lesson continues in the same lab as the lesson before it. If you don’t have it yet, download it here.
Download ch18-custom-libraries.zip
Or fetch and unpack it from a terminal in VS Code:
curl -fsSLO https://manykarim.github.io/robotframework-essentials-web/labs/ch18-custom-libraries.zip && unzip -q ch18-custom-libraries.zip && rm ch18-custom-libraries.zipInvoke-WebRequest https://manykarim.github.io/robotframework-essentials-web/labs/ch18-custom-libraries.zip -OutFile ch18-custom-libraries.zip; Expand-Archive ch18-custom-libraries.zip -DestinationPath .; Remove-Item ch18-custom-libraries.zipOpen the folder in VS Code with code rfe-ch18-custom-libraries (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-ch18-custom-libraries/
Directory.vscode/
- extensions.json
Directorychecks/
- smoke.robot
Directorylibraries/
- README.md
Directoryvariables/
- README.md
- .gitignore
- .python-version
- LAB.md
- pyproject.toml
- README.md
- requirements.txt
- uv.lock
Module functions are perfect when each keyword stands alone. But some keywords need to remember things between calls — a cart that accumulates items, a client that stays logged in. For that you write a class.
Robot Framework imports the class and calls its @keyword-decorated methods.
The @library(scope=...) decorator controls how long one instance lives:
scope="TEST" gives every test a fresh instance, so state never leaks
between tests. Reach for state only when keywords genuinely share it — a
stateless module library is simpler when it fits.
Task 1 — A cart that accumulates prices
Section titled “Task 1 — A cart that accumulates prices”Goal: create libraries/CartMath.py — a TEST-scoped class CartMath with
add_item_price(price) that stores a price and cart_total_should_be(expected)
that checks the running total. (The file name matches the class name — that is
how Robot Framework finds it.)
Here is the same shape over different data — a scoreboard. Adapt it to a cart of prices.
Worked example — adapt it, don’t copy it
from robot.api.deco import keyword, library
@library(scope="TEST")class ScoreBoard: def __init__(self): self.points = []
@keyword def add_points(self, points: int): self.points.append(points)
@keyword def score_should_be(self, expected: int): actual = sum(self.points) if actual != expected: raise AssertionError(f"Score {actual}, expected {expected}")Done when
libraries/CartMath.py defines a CartMath class whose methods are Robot keywords.
Hint 1
Decorate the class with @library(scope="TEST") and give it
def __init__(self): self.prices = [].
Hint 2
Decorate each method with @keyword. add_item_price appends to self.prices;
cart_total_should_be compares sum(self.prices) to expected and raises on a
mismatch.
Solution
from robot.api.deco import keyword, library
@library(scope="TEST")class CartMath: def __init__(self): self.prices = []
@keyword def add_item_price(self, price: float): self.prices.append(price)
@keyword def cart_total_should_be(self, expected: float): actual = sum(self.prices) if actual != expected: raise AssertionError(f"Cart total {actual}, expected {expected}")Task 2 — Import it and run it green
Section titled “Task 2 — Import it and run it green”Create checks/cart.robot:
*** Settings ***Library ../libraries/CartMath.py
*** Test Cases ***Cart Adds Up Add Item Price 19.90 Add Item Price 12.00 Cart Total Should Be 31.90Run it:
uv run robot checks/cart.robotrobot checks/cart.robotpoetry run robot checks/cart.robotDone when
the test Cart Adds Up passes.
Check your understanding
Quick recall. Your first answer stays in this browser; the quiz never blocks your progress.
@library(scope="TEST")means each test gets…TEST scope constructs a new instance per test, so state never leaks between tests.
Show the answer
Answer: a fresh library instance — TEST scope constructs a new instance per test, so state never leaks between tests.
How does Robot Framework find the class in a class library file?
CartMath.pymust contain a classCartMath; the file name matches the class.Show the answer
Answer: a class whose name matches the file name —
CartMath.pymust contain a classCartMath; the file name matches the class.
Key terms
- @library(scope=...)concept
- Control instance lifetime: TEST, SUITE, or GLOBAL.
- @keywordconcept
- Mark a method as a Robot Framework keyword.