Evaluating expressions¶
This appendix explains how expressions are evaluated using Python in different contexts and how variables in expressions are handled.
Introduction¶
Constructs such as IF/ELSE structures, WHILE loops and inline Python evaluation as well as several BuiltIn keywords accept an expression that is evaluated in Python:
Notice that instead of creating complicated expressions, it is often better to move the logic into a test library. That typically eases maintenance and also enhances execution speed.
Evaluation namespace¶
Expressions are evaluated using Python's eval function so that normal Python
constructs like '${x}' == 'expected', ${x} > 0 and
'${x}'.upper() not in ('FAIL', 'BAD') can be used and all
builtin functions like len() and int() are available.
In addition to that, all unrecognized Python variables are considered to be
modules that are automatically imported. It is possible to use all available
Python modules, including the standard modules and the installed third party
modules.
The following examples demonstrate using Python builtins as well as modules
using the inline Python evaluation syntax, but same expressions would also
work with IF/ELSE structures and BuiltIn keywords without the need to use
the ${{}} decoration around the expression:
A limitation of using modules is that nested modules like rootmod.submod
can only be used if the root module automatically imports the submodule. That is
not always the case and using such modules is not possible. An concrete example
that is relevant in the automation context is the selenium module that is
implemented, at least at the time of this writing, so that just importing
selenium does not import the selenium.webdriver submodule.
Another limitation is that modules cannot be used in the expression part of
a list comprehension. A workaround to both of these problems
is using the BuiltIn keyword Evaluate that accepts modules to be imported
and added to the evaluation namespace as an argument:
The Evaluate keyword also supports custom evaluation namespaces if further customization is needed. See its documentation in the BuiltIn library for more details.
Using variables¶
Normal ${variable} syntax¶
When a variable is used in the expression using the normal ${variable}
syntax, its value is replaced before the expression is evaluated. This
means that the value used in the expression will be the string
representation of the variable value, not the variable value itself.
This is not a problem with numbers and other objects that have a string
representation that can be evaluated directly. For example, if we have
a return code as an integer in variable ${rc}, using something like
${rc} > 0 is fine.
With other objects the behavior depends on the string representation.
Most importantly, strings must always be quoted either with
single or double quotes like '${x}', and if they can contain newlines, they must be
triple-quoted like '''${x}'''. Strings containing quotes themselves cause
additional problems, but triple-quoting typically handles them. Also the
backslash character \ is problematic, but can be handled by
using Python's raw-string notation like r'${path}'.
Special $variable syntax¶
Quoting strings is not that convenient, but there are cases where replacing the variable
with its string representation causes even bigger problems. For example, if the variable
value can be either a string or Python None, quoting like '${var}' is needed because
otherwise strings do not work, but then None is interpreted to be a string as well.
Luckily there is an easy solution to these problems discussed in this section.
Actual variables values are available in the evaluation namespace and can be accessed
using special variable syntax without the curly braces like $variable. Such variables
should never be quoted, not even if they contain strings.
Compare this these examples with the example in the previous section:
Using the $variable syntax slows down expression evaluation a little.
This should not typically matter, but should be taken into account if
complex expressions are evaluated often and there are strict time
constrains. Moving such logic to test libraries is typically a good idea
anyway.
Note
Due to technical reasons, these special variables are available during evaluation as local variables. That makes them unavailable in non-local scopes such as in the expression part of list comprehensions and inside lambdas.