Core concepts
Assertions
Almost every getter can assert, every assertion retries, and each return type allows different operators. The complete AssertionEngine reference.
Almost every keyword that gets something can also check it. There is no separate assertion library, and — this is the part that matters — the check retries.
Three things happen in that one line: the text is read, compared, and if it does not match yet, read again until it does or the retry window expires. The value is returned either way, so you can assert and capture at once:
Why this replaces the waiting code
The classic flaky test looks once, too early:
Three keywords, and still a race — the element became visible, then the text arrived a moment later. The retrying form has no gap between the look and the check, because they are one operation.
Two timeouts, and they are not the same
| Setting | Governs | Default |
|---|---|---|
Element never appears → timeout. Element is there but the text is still
Loading… → retry_assertions_for. Raising the wrong one is the most common
reason a fix does not help — and note that timeout bounds the whole keyword,
so retry_assertions_for=30s against the default timeout=10s still gives you
ten seconds of retrying.
The operators
Comparison
| Operator | Also written | True when |
|---|---|---|
Substring and edges
| Operator | Also written | True when |
|---|---|---|
^= and $= escape what you give them, so $= with .html matches a literal
dot, not any character.
matches — regular expressions
matches runs re.search over the value. It is spelled matches and nothing
else — it has no symbolic alias, as validate, then and not contains also
do not. In particular it is
not $: that character is the operator's internal value, Robot Framework
rejects it as input, and $= is a different operator entirely (ends with).
What matches returns depends on the groups in your pattern, and this catches
people out:
| Pattern has | Returns |
|---|---|
So matches is both an assertion and an extractor. Use re.search semantics:
it is not anchored unless you anchor it.
validate — any Python expression
When no operator says it, validate gives you one expression with the result
bound to value:
Getters that return a dictionary — Get BoundingBox with ALL, Get Viewport Size
— are usually asserted this way, indexing into value directly. So is
Get Browser Catalog, which returns a list of dictionaries and is not restricted
to the list operators — though the string ones (^=, $=, matches) raise a
TypeError on a list, so validate and then are what you actually use.
then — derive instead of check
then (also evaluate) asserts nothing. It evaluates an expression and returns
the result, so a getter hands back exactly the piece you want:
then never asserts, so it never retries — if the expression raises, the
keyword fails immediately. Use validate when you need the retry and then
only to reshape a value that is already there.
Which operators a keyword allows
Not every operator works everywhere. The engine picks a rule set from the type the keyword returns, and using the wrong one is an error, not a failed assertion.
Not every getter asserts, either. Get Element, Get Elements, Get Cookie,
Get Cookies, Get Device, Get Devices and a few others take no assertion
arguments at all — they only return.
| Return type | Allowed | Example keyword |
|---|---|---|
Two consequences worth knowing:
Lists are compared unordered. With == and != the engine sorts both sides
first, so this passes:
not contains does not work on lists — it is absent from the sequence
operators. Use validate instead:
Booleans use AssertionEngine's own truthiness, which is not quite Robot's.
Everything is true except FALSE, NO, OFF, 0, UNCHECKED, NONE and the
empty string, case-insensitively. So checked, yes and true are True;
unchecked, no and false are False. (unchecked is the one that differs
from Robot Framework's own is_truthy.)
Types must match
The expected value is used exactly as written — the library does not convert it. So it has to already be the type the keyword returns.
Keywords returning numbers do convert the expected value for you. Keywords
returning strings do not, and Get Text is the one everybody trips over.
Numbers also refuse the text operators outright — Get Element Count .row *= 2 raises ValueError: Operator 'contains' is not allowed. rather than
failing the assertion. When a
failure shows two values that look identical, print the types — see
messages below.
Comparing strings with < and >
Character by character, by code point, stopping at the first difference. Length never enters into it:
Both of the last two surprise people. 'abcde' < 'abd' because c precedes d
at the third character and the comparison stops there. '100.000' < '2' because
these are strings and '1' precedes '2' — nothing numeric is involved. You
cannot compare a number with a string in Python, and so not here either.
Formatters
A formatter normalises the value before it is compared, so a test does not fail
on whitespace nobody can see. Ordinary source indentation is not the problem —
Get Text reads rendered text, so the browser has already collapsed that. What
survives rendering is what bites: non-breaking spaces ( two spaces ), <pre> blocks, and text assembled from several inline
elements.
| Formatter | Does |
|---|---|
Set them per keyword:
That works on markup like <p>Hello World</p>. Without the
formatters the comparison sees "Hello\xa0\xa0World" — the browser collapses
ordinary whitespace but not non-breaking spaces — and fails.
They apply in the order given, and only to the value:
Add apply to expected and both sides get the same treatment, which is usually
what you meant:
Several keywords at once, and a scope:
The keyword returns the formatters that were set before, so a test can put them back — normalised to a canonical order rather than the order you supplied. A formatter written as a lambda is not included at all; only the named rules survive the round trip.
Custom messages
A failed assertion has a readable default. To replace it, message accepts four
placeholders:
| Placeholder | Is |
|---|---|
The two type placeholders exist precisely for the mismatch above — when both sides look the same and it still fails, printing the types shows why:
Common mistakes
| Symptom | Cause |
|---|---|
In short
- Assert inside the getter, not after it — the retry is the whole point.
- Match the operator to the return type: numbers refuse
containsandmatches, lists refusenot contains, booleans take only==and!=. retry_assertions_foris for values,timeoutis for elements.- Match the type.
Get Textreturns a string. - Lists take
==!=containsvalidatethen, and==ignores order. Dictionaries take the same set; the numeric ones also take>>=<<=, applied per key against a real dictionary. validatefor whatever the operators do not cover,thenwhen you want a value rather than a check,matcheswhen you want both — and it is only ever spelledmatches.- Reach for formatters before you reach for a regular expression.