Robot Framework User Guide-2
Robot Framework User Guide-2
Version 3.1.2
Copyright © 2008-2015 Nokia Networks
Copyright © 2016- Robot Framework Foundation
Licensed under the Creative Commons Attribution 3.0 Unported license
Table of Contents
1 Getting started
1.1 Introduction
1.2 Copyright and license
1.3 Installation instructions
1.4 Demonstrations
2 Creating test data
2.1 Test data syntax
2.2 Creating test cases
2.3 Creating tasks
2.4 Creating test suites
2.5 Using test libraries
2.6 Variables
2.7 Creating user keywords
2.8 Resource and variable files
2.9 Advanced features
3 Executing test cases
3.1 Basic usage
3.2 Test execution
3.3 Task execution
3.4 Post-processing outputs
3.5 Configuring execution
3.6 Created outputs
4 Extending Robot Framework
4.1 Creating test libraries
4.2 Remote library interface
4.3 Listener interface
4.4 Extending the Robot Framework Jar
5 Supporting Tools
5.1 Library documentation tool (Libdoc)
5.2 Test data documentation tool (Testdoc)
5.3 Test data clean-up tool (Tidy)
5.4 External tools
6 Appendices
6.1 All available settings in test data
6.2 All command line options
6.3 Documentation formatting
6.4 Time format
6.5 Boolean arguments
6.6 Internal API
1 Getting started
1.1 Introduction
1.2 Copyright and license
1.3 Installation instructions
1.4 Demonstrations
1.1 Introduction
Robot Framework is a Python-based, extensible keyword-driven automation framework for acceptance testing, acceptance test driven development (ATDD),
behavior driven development (BDD) and robotic process automation (RPA). It can be used in distributed, heterogeneous environments, where automation
requires using different technologies and interfaces.
The framework has a rich ecosystem around it consisting of various generic libraries and tools that are developed as separate projects. For more information
about Robot Framework and the ecosystem, see http://robotframework.org.
Robot Framework is open source software released under the Apache License 2.0. Its development is sponsored by the Robot Framework Foundation.
Note
The official RRA support was added in Robot Framework 3.1. This User Guide still talks mainly about creating tests, test data, and test libraries, but same concepts apply
also when creating tasks.
The test data is in simple, easy-to-edit tabular format. When Robot Framework is started, it processes the data, executes test cases and generates logs and
reports. The core framework does not know anything about the target under test, and the interaction with it is handled by libraries. Libraries can either use
application interfaces directly or use lower level test tools as drivers.
1.1.3 Screenshots
Following screenshots show examples of the test data and created reports and logs.
Test case files
Project pages
The number one place to find more information about Robot Framework and the rich ecosystem around it is http://robotframework.org. Robot Framework itself
is hosted on GitHub.
Mailing lists
There are several Robot Framework mailing lists where to ask and search for more information. The mailing list archives are open for everyone (including the
search engines) and everyone can also join these lists freely. Only list members can send mails, though, and to prevent spam new users are moderated which
means that it might take a little time before your first message goes through. Do not be afraid to send question to mailing lists but remember How To Ask
Questions The Smart Way.
robotframework-users
General discussion about all Robot Framework related issues. Questions and problems can be sent to this list. Used also for information sharing for all
users.
robotframework-announce
An announcements-only mailing list where only moderators can send messages. All announcements are sent also to the robotframework-users mailing
list so there is no need to join both lists.
robotframework-devel
Discussion about Robot Framework development.
http://www.apache.org/licenses/LICENSE-2.0
1.3.1 Introduction
1.3.2 Preconditions
Python 2 vs Python 3
Python installation
Jython installation
IronPython installation
PyPy installation
Configuring PATH
Setting https_proxy
1.3.3 Installing with pip
Installing pip for Python
Installing pip for Jython
Installing pip for IronPython
Installing pip for PyPy
Using pip
1.3.4 Installing from source
Getting source code
Installation
1.3.5 Standalone JAR distribution
1.3.6 Manual installation
1.3.7 Verifying installation
Where files are installed
1.3.8 Uninstallation
1.3.9 Upgrading
1.3.10 Executing Robot Framework
Using robot and rebot scripts
Executing installed robot module
Executing installed robot directory
1.3.11 Using virtual environments
1.3.1 Introduction
Robot Framework is implemented with Python and supports also Jython (JVM), IronPython (.NET) and PyPy. Before installing the framework, an obvious
precondition is installing at least one of these interpreters.
Different ways to install Robot Framework itself are listed below and explained more thoroughly in the subsequent sections.
Note
Prior to Robot Framework 3.0, there were also separate Windows installers for 32bit and 64bit Python versions. Because Python 2.7.9 and newer contain pip on Windows
and Python 3 would have needed two more installers, it was decided that Windows installers are not created anymore. The recommend installation approach also on
Windows is using pip.
1.3.2 Preconditions
Robot Framework is supported on Python (both Python 2 and Python 3), Jython (JVM) and IronPython (.NET) and PyPy. The interpreter you want to use
should be installed before installing the framework itself.
Which interpreter to use depends on the needed test libraries and test environment in general. Some libraries use tools or modules that only work with Python,
while others may use Java tools that require Jython or need .NET and thus IronPython. There are also many tools and libraries that run fine with all interpreters.
If you do not have special needs or just want to try out the framework, it is recommended to use Python. It is the most mature implementation, considerably
faster than Jython or IronPython (especially start-up time is faster), and also readily available on most UNIX-like operating systems. Another good alternative
is using the standalone JAR distribution that only has Java as a precondition.
Python 2 vs Python 3
Python 2 and Python 3 are mostly the same language, but they are not fully compatible with each others. The main difference is that in Python 3 all strings are
Unicode while in Python 2 strings are bytes by default, but there are also several other backwards incompatible changes. The last Python 2 release is Python 2.7
that was released in 2010 and will be supported until 2020. See Should I use Python 2 or 3? for more information about the differences, which version to use,
how to write code that works with both versions, and so on.
Robot Framework 3.0 is the first Robot Framework version to support Python 3. It supports also Python 2, and the plan is to continue Python 2 support as long
as Python 2 itself is officially supported. We hope that authors of the libraries and tools in the wider Robot Framework ecosystem also start looking at Python 3
support now that the core framework supports it.
Python installation
On most UNIX-like systems such as Linux and OS X you have Python installed by default. If you are on Windows or otherwise need to install Python yourself,
a good place to start is http://python.org. There you can download a suitable installer and get more information about the installation process and Python in
general.
Robot Framework 3.0 supports Python 2.6, 2.7, 3.3 and newer, but the plan is to drop Python 2.6 and 3.3 support in RF 3.1. If you need to use older versions,
Robot Framework 2.5-2.8 support Python 2.5 and Robot Framework 2.0-2.1 support Python 2.3 and 2.4.
After installing Python, you probably still want to configure PATH to make Python itself as well as the robot and rebot runner scripts executable on the
command line.
Tip
Latest Python Windows installers allow setting PATH as part of the installation. This is disabled by default, but Add python.exe to Path can be enabled on the
Customize Python screen.
Jython installation
Using test libraries implemented with Java or that use Java tools internally requires running Robot Framework on Jython, which in turn requires Java Runtime
Environment (JRE) or Java Development Kit (JDK). Installing either of these Java distributions is out of the scope of these instructions, but you can find more
information, for example, from http://java.com.
Installing Jython is a fairly easy procedure, and the first step is getting an installer from http://jython.org. The installer is an executable JAR package, which you
can run from the command line like java -jar jython_installer-<version>.jar. Depending on the system configuration, it may also be possible to just
double-click the installer.
Robot Framework 3.0 supports Jython 2.7 which requires Java 7 or newer. If older Jython or Java versions are needed, Robot Framework 2.5-2.8 support
Jython 2.5 (requires Java 5 or newer) and Robot Framework 2.0-2.1 support Jython 2.2.
After installing Jython, you probably still want to configure PATH to make Jython itself as well as the robot and rebot runner scripts executable on the
command line.
IronPython installation
IronPython allows running Robot Framework on the .NET platform and interacting with C# and other .NET languages and APIs. Only IronPython 2.7 is
supported in general and IronPython 2.7.9 or newer is highly recommended.
If not using IronPython 2.7.9 or newer and Robot Framework 3.1 or newer, an additional requirement is installing ElementTree module 1.2.7 preview release.
This is required because the ElementTree module distributed with older IronPython versions was broken. Once you have pip activated for IronPython, you can
easily install ElementTree using this command:
Alternatively you can download the zip package, extract it, and install it by running ipy setup.py install on the command prompt in the created directory.
After installing IronPython, you probably still want to configure PATH to make IronPython itself as well as the robot and rebot runner scripts executable on
the command line.
PyPy installation
PyPy is an alternative implementation of the Python language with both Python 2 and Python 3 compatible versions available. Its main advantage over the
standard Python implementation is that it can be faster and use less memory, but this depends on the context where and how it is used. If execution speed is
important, at least testing PyPY is probably a good idea.
Installing PyPy is a straightforward procedure and you can find both installers and installation instructions at http://pypy.org. After installation you probably
still want to configure PATH to make PyPy itself as well as the robot and rebot runner scripts executable on the command line.
Configuring PATH
The PATH environment variable lists locations where commands executed in a system are searched from. To make using Robot Framework easier from the
command prompt, it is recommended to add the locations where the runner scripts are installed into the PATH. It is also often useful to have the interpreter itself
in the PATH to make executing it easy.
When using Python on UNIX-like machines both Python itself and scripts installed with should be automatically in the PATH and no extra actions needed. On
Windows and with other interpreters the PATH must be configured separately.
Tip
Latest Python Windows installers allow setting PATH as part of the installation. This is disabled by default, but Add python.exe to Path can be enabled on the
Customize Python screen. It will add both the Python installation directory and the Scripts directory to the PATH.
What directories you need to add to the PATH depends on the interpreter and the operating system. The first location is the installation directory of the interpreter
(e.g. C:\Python27) and the other is the location where scripts are installed with that interpreter. Both Python and IronPython install scripts to Scripts directory
under the installation directory on Windows (e.g. C:\Python27\Scripts) and Jython uses bin directory regardless the operating system (e.g. C:\jython2.7.0\bin).
Notice that the Scripts and bin directories may not be created as part of the interpreter installation, but only later when Robot Framework or some other third
party module is installed.
On Windows you can configure PATH by following the steps below. Notice that the exact setting names may be different on different Windows versions, but the
basic approach should still be the same.
1. Open Control Panel > System > Advanced > Environment Variables. There are User variables and System variables, and the difference
between them is that user variables affect only the current users, whereas system variables affect all users.
2. To edit an existing PATH value, select Edit and add ;<InstallationDir>;<ScriptsDir> at the end of the value (e.g.
;C:\Python27;C:\Python27\Scripts). Note that the semicolons (;) are important as they separate the different entries. To add a new PATH value, select
New and set both the name and the value, this time without the leading semicolon.
3. Exit the dialog with Ok to save the changes.
4. Start a new command prompt for the changes to take effect.
Notice that if you have multiple Python versions installed, the executed robot or rebot runner script will always use the one that is first in the PATH regardless
under what Python version that script is installed. To avoid that, you can always execute the installed robot module directly like C:\Python27\python.exe -m
robot.
Notice also that you should not add quotes around directories you add into the PATH (e.g. "C:\Python27\Scripts"). Quotes can cause problems with Python
programs and they are not needed in this context even if the directory path would contain spaces.
On UNIX-like systems you typically need to edit either some system wide or user specific configuration file. Which file to edit and how depends on the system,
and you need to consult your operating system documentation for more details.
Setting https_proxy
If you are installing with pip and are behind a proxy, you need to set the https_proxy environment variable. It is needed both when installing pip itself and
when using it to install Robot Framework and other Python packages.
How to set the https_proxy depends on the operating system similarly as configuring PATH. The value of this variable must be an URL of the proxy, for
example, http://10.0.0.42:8080.
Latest Python, Jython, IronPython and PyPy versions contain pip bundled in. Which versions contain it and how to possibly activate it is discussed in sections
below. See pip project pages if for the latest installation instructions if you need to install it.
Note
Robot Framework 3.1 and newer are distributed as wheels, but earlier versions are available only as source distributions in tar.gz format. It is possible to install both using
pip, but installing wheels is a lot faster.
Note
Only Robot Framework 2.7 and newer can be installed using pip. If you need an older version, you must use other installation approaches.
Starting from Python 2.7.9, the standard Windows installer by default installs and activates pip. Assuming you also have configured PATH and possibly set
https_proxy, you can run pip install robotframework right after Python installation. With Python 3.4 and newer pip is officially part of the interpreter and
should be automatically available.
Outside Windows and with older Python versions you need to install pip yourself. You may be able to do it using system package managers like Apt or Yum on
Linux, but you can always use the manual installation instructions found from the pip project pages.
If you have multiple Python versions with pip installed, the version that is used when the pip command is executed depends on which pip is first in the PATH.
An alternative is executing the pip module using the selected Python version directly:
Jython 2.7 contain pip bundled in, but it needs to be activated before using it by running the following command:
jython -m ensurepip
Jython installs its pip into <JythonInstallation>/bin directory. Does running pip install robotframework actually use it or possibly some other pip version
depends on which pip is first in the PATH. An alternative is executing the pip module using Jython directly:
IronPython 2.7.5 and newer contain pip bundled in. With IronPython 2.7.9 and newer pip also works out-of-the-box, but with earlier versions it needs to be
activated with ipy -m ensurepip similarly as with Jython.
With IronPython 2.7.7 and earlier you need to use -X:Frames command line option when activating pip like ipy -X:Frames -m ensurepip and also when
using it. Prior to IronPython 2.7.9 there were problems creating possible start-up scripts when installing modules. Using IronPython 2.7.9 is highly
recommended.
IronPython installs pip into <IronPythonInstallation>/Scripts directory. Does running pip install robotframework actually use it or possibly some other pip
version depends on which pip is first in the PATH. An alternative is executing the pip module using IronPython directly:
Also PyPy contains pip bundled in. It is not activated by default, but it can be activated similarly as with the other interpreters:
pypy -m ensurepip
pypy3 -m ensurepip
If you have multiple Python versions with pip installed, the version that is used when the pip command is executed depends on which pip is first in the PATH.
An alternative is executing the pip module using PyPy directly:
pypy -m pip
pypy3 -m pip
Using pip
Once you have pip installed, and have set https_proxy if you are behind a proxy, using pip on the command line is very easy. The easiest way to use pip is by
letting it find and download packages it installs from the Python Package Index (PyPI), but it can also install packages downloaded from the PyPI separately.
The most common usages are shown below and pip documentation has more information and examples.
# Uninstall
pip uninstall robotframework
Notice that pip 1.4 and newer will only install stable releases by default. If you want to install an alpha, beta or release candidate, you need to either specify the
version explicitly or use the --pre option:
Notice that on Windows pip, by default, does not recreate robot.bat and rebot.bat start-up scripts if the same Robot Framework version is installed multiple
times using the same Python version. This mainly causes problems when using virtual environments, but is something to take into account also if doing custom
installations using pip. A workaround if using the --no-cache-dir option like pip install --no-cache-dir robotframework. Alternatively it is possible to
ignore the start-up scripts altogether and just use python -m robot and python -m robot.rebot commands instead.
You typically get the source code by downloading a source distribution from PyPI. Starting from Robot Framework 3.1 the source distribution is a zip package
and with earlier versions it is in tar.gz format. Once you have downloaded the package, you need to extract it somewhere and, as a result, you get a directory
named robotframework-<version>. The directory contains the source code and a setup.py script needed for installing it.
An alternative approach for getting the source code is cloning project's GitHub repository directly. By default you will get the latest code, but you can easily
switch to different released versions or other tags.
Installation
Robot Framework is installed from source using Python's standard setup.py script. The script is in the directory containing the sources and you can run it from
the command line using any of the supported interpreters:
The setup.py script accepts several arguments allowing, for example, installation into a non-default location that does not require administrative rights. It is
also used for creating different distribution packages. Run python setup.py --help for more details.
1.3.5 Standalone JAR distribution
Robot Framework is also distributed as a standalone Java archive that contains both Jython and Robot Framework and only requires Java a dependency. It is an
easy way to get everything in one package that requires no installation, but has a downside that it does not work with the normal Python interpreter.
The package is named robotframework-<version>.jar and it is available on the Maven central. After downloading the package, you can execute tests with it
like:
If you want to post-process outputs using Rebot or use other built-in supporting tools, you need to give the command name rebot, libdoc, testdoc or tidy as
the first argument to the JAR file:
For more information about the different commands, execute the JAR without arguments.
In addition to the Python standard library and Robot Framework modules, the standalone JAR versions starting from 2.9.2 also contain the PyYAML
dependency needed to handle yaml variable files.
1. Get the source code. All the code is in a directory (a package in Python) called robot. If you have a source distribution or a version control checkout, you
can find it from the src directory, but you can also get it from an earlier installation.
2. Copy the source code where you want to.
3. Decide how to run tests.
$ robot --version
Robot Framework 3.0 (Python 2.7.10 on linux2)
$ rebot --version
Rebot 3.0 (Python 2.7.10 on linux2)
If running the runner scripts fails with a message saying that the command is not found or recognized, a good first step is double-checking the PATH
configuration. If that does not help, it is a good idea to re-read relevant sections from these instructions before searching help from the Internet or as asking help
on robotframework-users mailing list or elsewhere.
When an automatic installer is used, Robot Framework source code is copied into a directory containing external Python modules. On UNIX-like operating
systems where Python is pre-installed the location of this directory varies. If you have installed the interpreter yourself, it is normally Lib/site-packages under
the interpreter installation directory, for example, C:\Python27\Lib\site-packages. The actual Robot Framework code is in a directory named robot.
Robot Framework runner scripts are created and copied into another platform-specific location. When using Python on UNIX-like systems, they normally go to
/usr/bin or /usr/local/bin. On Windows and with Jython and IronPython, the scripts are typically either in Scripts or bin directory under the interpreter
installation directory.
1.3.8 Uninstallation
The easiest way to uninstall Robot Framework is using pip:
A nice feature in pip is that it can uninstall packages even if they are installed from the source. If you do not have pip available or have done a manual
installation to a custom location, you need to find where files are installed and remove them manually.
If you have set PATH or configured the environment otherwise, you need to undo those changes separately.
1.3.9 Upgrading
If you are using pip, upgrading to a new version requires either specifying the version explicitly or using the --upgrade option. If upgrading to a preview
release, --pre option is needed as well.
# Upgrade to the latest stable version. This is the most common method.
pip install --upgrade robotframework
When using pip, it automatically uninstalls previous versions before installation. If you are installing from source, it should be safe to just install over an
existing installation. If you encounter problems, uninstallation before installation may help.
When upgrading Robot Framework, there is always a change that the new version contains backwards incompatible changes affecting existing tests or test
infrastructure. Such changes are very rare in minor versions like 2.8.7 or 2.9.2, but more common in major versions like 2.9 and 3.0. Backwards incompatible
changes and deprecated features are explained in the release notes, and it is a good idea to study them especially when upgrading to a new major version.
Starting from Robot Framework 3.0, tests are executed using the robot script and results post-processed with the rebot script:
robot tests.robot
rebot output.xml
Both of these scripts are installed as part of the normal installation and can be executed directly from the command line if PATH is set correctly. They are
implemented using Python except on Windows where they are batch files.
Older Robot Framework versions do not have the robot script and the rebot script is installed only with Python. Instead they have interpreter specific scripts
pybot, jybot and ipybot for test execution and jyrebot and ipyrebot for post-processing outputs. These scripts still work, but they will be deprecated and
removed in the future.
An alternative way to run tests is executing the installed robot module or its sub module robot.run directly using Python's -m command line option. This is
especially useful if Robot Framework is used with multiple Python versions:
The support for python -m robot approach is a new feature in Robot Framework 3.0, but the older versions support python -m robot.run. The latter must
also be used with Python 2.6.
Post-processing outputs using the same approach works too, but the module to execute is robot.rebot:
If you know where Robot Framework is installed, you can also execute the installed robot directory or the run.py file inside it directly:
Running the directory is a new feature in Robot Framework 3.0, but the older versions support running the robot/run.py file.
Post-processing outputs using the robot/rebot.py file works the same way too:
Executing Robot Framework this way is especially handy if you have done a manual installation.
Robot Framework in general works fine with virtual environments. The only problem is that when using pip on Windows, robot.bat and rebot.bat scripts are
not recreated by default. This means that if Robot Framework is installed into multiple virtual environments, the robot.bat and rebot.bat scripts in the latter
ones refer to the Python installation in the first virtual environment. A workaround is using the --no-cache-dir option when installing. Alternatively the start-
up scripts can be ignored and python -m robot and python -m robot.rebot commands used instead.
1.4 Demonstrations
There are several demo projects that introduce Robot Framework and help getting started with it.
Different sections are recognized by their header row. The recommended header format is *** Settings ***, but the header is case-insensitive, surrounding
spaces are optional, and the number of asterisk characters can vary as long as there is one asterisk in the beginning. In addition to using the plural format, also
singular variants like Setting and Test Case are accepted. In other words, also *setting would be recognized as a section header.
The header row can contain also other data than the actual section header. The extra data must be separated from the section header using the data format
dependent separator, typically two or more spaces. These extra headers are ignored at parsing time, but they can be used for documenting purposes. This is
especially useful when creating test cases using the data-driven style.
Note
Prior to Robot Framework 3.1, section names were space-insensitive, meaning that spaces could be removed (e.g. TestCases) or extra spaces added (e.g. S e t t i n
g s). This is now deprecated and only the format in the table above, case-insensitively, is supported.
Note
Prior to Robot Framework 3.1, all unrecognized sections were silently ignored but nowadays they cause an error. Comments sections can be used if sections not
containing actual test data are needed.
1. The most common approach is using the plain text format and store files using the .robot extension. Alternatively it is possible to use the .txt extension.
2. The TSV format can be used as long as files are compatible with the plain text format.
3. Plain text test data can be embedded into reStructuredText files.
4. Earlier Robot Framework versions supported test data in HTML format.
Prior to Robot Framework 3.1, all aforementioned file formats were parsed automatically unless the --extension option was used to limit parsing. In Robot
Framework 3.1 automatically parsing other than *.robot files was deprecated, and in the future other files are parsed only if that is explicitly configured using
the --extension option. The support for the HTML format has bee deprecated in general it will be removed altogether in the future.
The plain text format is the base for all supported Robot Framework data formats. Test data is parsed line by line, but long logical lines can be split if needed. In
a single line different data items like keywords and their arguments are separated from each others using a separator. The most commonly used separator is two
or more spaces, but it is also possible to use a pipe character surrounded with spaces (|). Depending on the separator we can talk about the space separated
format and the pipe separated format, but same file can actually contain lines with both separators.
Possible literal tab characters are converted to two spaces before parsing lines otherwise. This allows using a single tab as a separator instead of multiple
spaces. Notice, however, that multiple consecutive tabs are still considered to be a single separator. If an actual tab character is needed in the data, it must be
escaped like \t.
Plain text files containing non-ASCII characters must be saved using the UTF-8 encoding.
In the space separated format two or more spaces (or one or more tab characters) act as a separator between different data items. The number of spaces used as
separator can vary, as long as there are at least two, making it possible to align the data nicely in settings and elsewhere if it makes sense.
*** Settings ***
Documentation Example using the space separated plain text format.
Library OperatingSystem
Another Test
Should Be Equal ${MESSAGE} Hello, world!
Because space is used as separator, all empty items and items containing only spaces must be escaped with backslashes or with built-in ${EMPTY} and ${SPACE}
variables, respectively.
Tip
Although using two spaces as a separator is enough, it is recommend to use four spaces to make the separator easier to notice.
The biggest problem of the space delimited format is that visually separating keywords from arguments can be tricky. This is a problem especially if keywords
take a lot of arguments and/or arguments contain spaces. In such cases the pipe delimited variant can work better because it makes the separator more visible.
One file can contain both space separated and pipe separated lines. Pipe separated lines are recognized by the mandatory leading pipe character, but the pipe at
the end of the line is optional. There must always be at least one space on both sides of the pipe except at the beginning and at the end of the line. There is no
need to align the pipes, but that often makes the data easier to read.
There is no need to escape empty cells (other than the trailing empty cells) when using the pipe separated format. Possible pipes surrounded by spaces in the
actual test data must be escaped with a backslash, though:
Editing
Plain text files can be easily edited using normal text editors and IDEs. Many of these tools also have plugins that support syntax highlighting Robot
Framework test data and may also provide other features such as keyword completion. Robot Framework specific editors like RIDE naturally support the plain
text format as well.
As already mentioned, plain text files containing non-ASCII characters must be saved using the UTF-8 encoding.
Recognized extensions
The recommended extension for test case files in the plain text format is .robot. Files using this extension are parsed automatically. Also the .txt extension can
be used, but starting from Robot Framework 3.1 the --extension option must be used to explicitly tell that these files should be parsed.
When creating resource files, it is possible to use the special .resource extension in addition to the aforementioned .robot and .txt extensions. This way resource
files and test cases files are easily separated from each others.
Note
TSV format
Files in the tab-separated values (TSV) format are typically edited in spreadsheet programs and, because the syntax is so simple, they are easy to generate
programmatically. They are also pretty easy to edit using normal text editors and they work well in version control, but the plain text format is even better
suited for these purposes.
The TSV format and the space separated variant of the plain text format are nearly identical, but earlier Robot Framework versions had slightly different parser
for these formats. The differences were:
The TSV parser did not require escaping empty intermediate cells.
The TSV parser removed possible quotes around cells that may be added by spreadsheet programs.
The TSV parser was deprecated in Robot Framework 3.1 and it will be removed in the future. It is still possible to use the TSV format, but files must be fully
compatible with the plain text format. This basically requires escaping all empty cells and configuring spreadsheet program or other tool saving TSV files not to
add surrounding quotes to cells.
You can create and edit TSV files in any spreadsheet program, such as Microsoft Excel. Select the tab-separated format when you save the file. It is also a good
idea to turn all automatic corrections off and configure the tool to treat all values in the file as plain text. As explained above, TSV files should also be saved so
that no quotes are added around the cells.
TSV files are relatively easy to edit with any text editor, especially if the editor supports visually separating tabs from spaces. The TSV format is also supported
by RIDE.
Like plain text files, TSV files containing non-ASCII characters must be saved using the UTF-8 encoding.
Recognized extensions
Files in the TSV format are customarily saved using the .tsv extension, but starting from Robot Framework 3.1 the --extension option must be used to
explicitly tell that these files should be parsed. Another possibility is saving also these files using the the .robot extension, but this requires the file to be fully
compatible with the plain text syntax.
reStructuredText format
reStructuredText (reST) is an easy-to-read plain text markup syntax that is commonly used for documentation of Python projects (including Python itself, as
well as this User Guide). reST documents are most often compiled to HTML, but also other output formats are supported.
Using reST with Robot Framework allows you to mix richly formatted documents and test data in a concise text format that is easy to work with using simple
text editors, diff tools, and source control systems.
When using reST files with Robot Framework, test data is defined using code blocks. Earlier Robot Framework versions also supported using tables and
converting reST files to HTML, but this was deprecated in Robot Framework 3.1.
Note
Using reST files with Robot Framework requires the Python docutils module to be installed.
reStructuredText documents can contain code examples in so called code blocks. When these documents are compiled into HTML or other formats, the code
blocks are syntax highlighted using Pygments. In standard reST code blocks are started using the code directive, but Sphinx uses code-block or sourcecode
instead. The name of the programming language in the code block is given as an argument to the directive. For example, following code blocks contain Python
and Robot Framework examples, respectively:
.. code:: python
def example_keyword():
print('Hello, world!')
.. code:: robotframework
When Robot Framework parses reStructuredText files, it first searches for possible code, code-block or sourcecode blocks containing Robot Framework test
data. If such code blocks are found, data they contain is written into an in-memory file and executed. All data outside the code blocks is ignored.
The test data in the code blocks must be defined using the plain text format. As the example below illustrates, both space and pipe separated variants are
supported:
Example
-------
.. code:: robotframework
Another Test
Should Be Equal ${MESSAGE} Hello, world!
Also this text is outside code blocks and ignored. Above block used
the space separated plain text format and the block below uses the pipe
separated variant.
.. code:: robotframework
Using tables
Earlier Robot Framework versions supported using reStructuredText also so that test data was defined in tables. These files were then internally converted to
HTML format before parsing them. This functionality was deprecated in Robot Framework 3.1 and will be removed in the future along with the general support
for the HTML format.
Editing
Test data in reStructuredText files can be edited with any text editor, and many editors also provide automatic syntax highlighting for it.
Robot Framework requires reST files containing non-ASCII characters to be saved using the UTF-8 encoding.
Recognized extensions
Robot Framework supports reStructuredText files using both .rst and .rest extension. Starting from Robot Framework 3.1 the --extension option must be used
to explicitly tell that these files should be parsed.
When Robot Framework parses reStructuredText files, errors below level SEVERE are ignored to avoid noise about possible non-standard directives and other
such markup. This may hide also real errors, but they can be seen when processing files using reStructuredText tooling normally.
HTML format
Earlier Robot Framework versions supported test data in HTML format but this support has been deprecated in Robot Framework 3.1. All test data in HTML
format should be converted to the plain text format or other supported formats. This is typically easiest by using the built-in Tidy tool.
Ignored data
All data before the first test data section. If the data format allows data between sections, also that is ignored.
Data in the Comments section.
All empty rows.
All empty cells at the end of rows, unless they are escaped.
All single backslashes (\) when not used for escaping.
All characters following the hash character (#), when it is the first character of a cell. This means that hash marks can be used to enter comments in the
test data.
When Robot Framework ignores some data, this data is not available in any resulting reports and, additionally, most tools used with Robot Framework also
ignore them. To add information that is visible in Robot Framework outputs, place it to the documentation or other metadata of test cases or suites, or log it with
the BuiltIn keywords Log or Comment.
Escaping
The escape character in Robot Framework test data is the backslash (\) and additionally built-in variables ${EMPTY} and ${SPACE} can often be used for
escaping. Different escaping mechanisms are discussed in the sections below.
The backslash character can be used to escape special characters so that their literal values are used.
The backslash character also allows creating special escape sequences that are recognized as characters that would otherwise be hard or impossible to create in
the test data.
Escape sequences
Sequence Meaning Examples
\n Newline character. first line\n2nd line
\r Carriage return character text\rmore text
\t Tab character. text\tmore text
\xhh Character with hex value hh. null byte: \x00, ä: \xE4
\uhhhh Character with hex value hhhh. snowman: \u2603
\Uhhhhhhhh Character with hex value hhhhhhhh. love hotel: \U0001f3e9
Note
All strings created in the test data, including characters like \x02, are Unicode and must be explicitly converted to byte strings if needed. This can be done, for example,
using Convert To Bytes or Encode String To Bytes keywords in BuiltIn and String libraries, respectively, or with something like value.encode('UTF-8') in Python
code.
Note
If invalid hexadecimal values are used with \x, \u or \U escapes, the end result is the original value without the backslash character. For example, \xAX (not hex) and
\U00110000 (too large value) result with xAX and U00110000, respectively. This behavior may change in the future, though.
Note
Built-in variable ${\n} can be used if operating system dependent line terminator is needed (\r\n on Windows and \n elsewhere).
Note
Possible un-escaped whitespace character after the \n is ignored. This means that two lines\nhere and two lines\n here are equivalent. The motivation for this is
to allow wrapping long lines containing newlines when using the HTML format, but the same logic is used also with other formats. An exception to this rule is that the
whitespace character is not ignored inside the extended variable syntax.
If empty values are needed as arguments for keywords or otherwise, they often need to be escaped to prevent them from being ignored. Empty trailing cells
must be escaped regardless of the test data format, and when using the space separated format all empty values must be escaped.
Empty cells can be escaped either with the backslash character or with built-in variable ${EMPTY}. The latter is typically recommended as it is easier to
understand. All these cases are illustrated by the following examples:
Handling spaces
Spaces, especially consecutive spaces, as part of arguments for keywords or needed otherwise are problematic for two reasons:
Two or more consecutive spaces is considered a separator when using the space separated format.
Leading and trailing spaces are ignored when using the pipe separated format.
In these cases spaces need to be escaped. Similarly as when escaping empty cells, it is possible to do that either by using the backslash character or by using the
built-in variable ${SPACE}.
As the above examples show, using the ${SPACE} variable often makes the test data easier to understand. It is especially handy in combination with the
extended variable syntax when more than one space is needed.
If there is more data than readily fits a row, it possible to use ellipsis (...) to continue the previous line. In test case and keyword tables, the ellipsis must be
preceded by at least one empty cell. In settings and variable tables, it can be placed directly under the setting or variable name. In all tables, all empty cells
before the ellipsis are ignored.
Also suite, test or keyword documentation and value of test suite metadata can be too long to fit into one row nicely. These values can be split into multiple
rows as well, and they will be joined together with newlines.
All the syntax discussed above is illustrated in the following examples. In the first three tables test data has not been split, and the following three illustrate how
fewer columns are needed after splitting the data to several rows.
When using Robot Framework for other automation purposes than test automation, it is recommended to create tasks instead of tests. The task syntax is for
most parts identical to the test syntax, and the differences are explained in the Creating tasks section.
Basic syntax
Test cases are constructed in test case tables from the available keywords. Keywords can be imported from test libraries or resource files, or created in the
keyword table of the test case file itself.
The first column in the test case table contains test case names. A test case starts from the row with something in this column and continues to the next test case
name or to the end of the table. It is an error to have something between the table headers and the first test.
The second column normally has keyword names. An exception to this rule is setting variables from keyword return values, when the second and possibly also
the subsequent columns contain variable names and a keyword name is located after them. In either case, columns after the keyword name contain possible
arguments to the specified keyword.
Setting Variables
Do Something first argument second argument
${value} = Get Some Value
Should Be Equal ${value} Expected value
Note
Although test case names can contain any character, using ? and especially * is not generally recommended because they are considered to be wildcards when selecting
test cases. For example, trying to run only a test with name Example * like --test 'Example *' will actually run any test starting with Example.
Test cases can also have their own settings. Setting names are always in the second column, where keywords normally are, and their values are in the
subsequent columns. Setting names have square brackets around them to distinguish them from keywords. The available settings are listed below and explained
later in this section.
[Documentation]
Used for specifying a test case documentation.
[Tags]
Used for tagging test cases.
[Setup], [Teardown]
Specify test setup and teardown.
[Template]
Specifies the template keyword to use. The test itself will contain only data to use as arguments to that keyword.
[Timeout]
Used for setting a test case timeout. Timeouts are discussed in their own section.
Note
Setting names are case-insensitive, but the format used above is recommended. Prior to Robot Framework 3.1, settings were also space-insensitive meaning that extra
spaces could be added (e.g. [T a g s]). This is now deprecated and only the format above, case-insensitively, is supported. Possible space between brackets and the
name (e.g. [ Tags ]) is still allowed.
The Setting table can have the following test case related settings. These settings are mainly default values for the test case specific settings listed earlier.
Keywords can accept zero or more arguments, and some arguments may have default values. What arguments a keyword accepts depends on its
implementation, and typically the best place to search this information is keyword's documentation. In the examples in this section the documentation is
expected to be generated using the Libdoc tool, but the same information is available on documentation generated by generic documentation tools such as
javadoc.
Positional arguments
Most keywords have a certain number of arguments that must always be given. In the keyword documentation this is denoted by specifying the argument
names separated with a comma like first, second, third. The argument names actually do not matter in this case, except that they should explain what the
argument does, but it is important to have exactly the same number of arguments as specified in the documentation. Using too few or too many arguments will
result in an error.
The test below uses keywords Create Directory and Copy File from the OperatingSystem library. Their arguments are specified as path and source,
destination, which means that they take one and two arguments, respectively. The last keyword, No Operation from BuiltIn, takes no arguments.
Default values
Arguments often have default values which can either be given or not. In the documentation the default value is typically separated from the argument name
with an equal sign like name=default value, but with keywords implemented using Java there may be multiple implementations of the same keyword with
different arguments instead. It is possible that all the arguments have default values, but there cannot be any positional arguments after arguments with default
values.
Using default values is illustrated by the example below that uses Create File keyword which has arguments path, content=, encoding=UTF-8. Trying to use
it without any arguments or more than three arguments would not work.
It is also possible that a keyword accepts any number of arguments. These so called varargs can be combined with mandatory arguments and arguments with
default values, but they are always given after them. In the documentation they have an asterisk before the argument name like *varargs.
For example, Remove Files and Join Paths keywords from the OperatingSystem library have arguments *paths and base, *parts, respectively. The former
can be used with any number of arguments, but the latter requires at least one argument.
Named arguments
The named argument syntax makes using arguments with default values more flexible, and allows explicitly labeling what a certain argument value means.
Technically named arguments work exactly like keyword arguments in Python.
Basic syntax
It is possible to name an argument given to a keyword by prefixing the value with the name of the argument like arg=value. This is especially useful when
multiple arguments have default values, as it is possible to name only some the arguments and let others use their defaults. For example, if a keyword accepts
arguments arg1=a, arg2=b, arg3=c, and it is called with one argument arg3=override, arguments arg1 and arg2 get their default values, but arg3 gets
value override. If this sounds complicated, the named arguments example below hopefully makes it more clear.
The named argument syntax is both case and space sensitive. The former means that if you have an argument arg, you must use it like arg=value, and neither
Arg=value nor ARG=value works. The latter means that spaces are not allowed before the = sign, and possible spaces after it are considered part of the given
value.
When the named argument syntax is used with user keywords, the argument names must be given without the ${} decoration. For example, user keyword with
arguments ${arg1}=first, ${arg2}=second must be used like arg2=override.
Using normal positional arguments after named arguments like, for example, | Keyword | arg=value | positional |, does not work. The relative order of
the named arguments does not matter.
It is possible to use variables in both named argument names and values. If the value is a single scalar variable, it is passed to the keyword as-is. This allows
using any objects, not only strings, as values also when using the named argument syntax. For example, calling a keyword like arg=${object} will pass the
variable ${object} to the keyword without converting it to a string.
If variables are used in named argument names, variables are resolved before matching them against argument names.
The named argument syntax requires the equal sign to be written literally in the keyword call. This means that variable alone can never trigger the named
argument syntax, not even if it has a value like foo=bar. This is important to remember especially when wrapping keywords into other keywords. If, for
example, a keyword takes a variable number of arguments like @{args} and passes all of them to another keyword using the same @{args} syntax, possible
named=arg syntax used in the calling side is not recognized. This is illustrated by the example below.
If keyword needs to accept and pass forward any named arguments, it must be changed to accept free named arguments. See free named argument examples for
a wrapper keyword version that can pass both positional and named arguments forward.
The named argument syntax is used only when the part of the argument before the equal sign matches one of the keyword's arguments. It is possible that there
is a positional argument with a literal value like foo=quux, and also an unrelated argument with name foo. In this case the argument foo either incorrectly gets
the value quux or, more likely, there is a syntax error.
In these rare cases where there are accidental matches, it is possible to use the backslash character to escape the syntax like foo\=quux. Now the argument will
get a literal value foo=quux. Note that escaping is not needed if there are no arguments with name foo, but because it makes the situation more explicit, it may
nevertheless be a good idea.
As already explained, the named argument syntax works with keywords. In addition to that, it also works when importing libraries.
Naming arguments is supported by user keywords and by most test libraries. The only exception are Java based libraries that use the static library API. Library
documentation generated with Libdoc has a note does the library support named arguments or not.
The following example demonstrates using the named arguments syntax with library keywords, user keywords, and when importing the Telnet test library.
Robot Framework supports free named arguments, often also called free keyword arguments or kwargs, similarly as Python supports **kwargs. What this
means is that a keyword can receive all arguments that use the named argument syntax (name=value) and do not match any arguments specified in the signature
of the keyword.
Free named arguments are supported by same keyword types than normal named arguments. How keywords specify that they accept free named arguments
depends on the keyword type. For example, Python based keywords simply use **kwargs and user keywords use &{kwargs}.
Free named arguments support variables similarly as named arguments. In practice that means that variables can be used both in names and values, but the
escape sign must always be visible literally. For example, both foo=${bar} and ${foo}=${bar} are valid, as long as the variables that are used exist. An extra
limitation is that free argument names must always be strings.
Examples
As the first example of using free named arguments, let's take a look at Run Process keyword in the Process library. It has a signature command, *arguments,
**configuration, which means that it takes the command to execute (command), its arguments as variable number of arguments (*arguments) and finally
optional configuration parameters as free named arguments (**configuration). The example below also shows that variables work with free keyword
arguments exactly like when using the named argument syntax.
See Free keyword arguments (**kwargs) section under Creating test libraries for more information about using the free named arguments syntax in your
custom test libraries.
As the second example, let's create a wrapper user keyword for running the program.py in the above example. The wrapper keyword Run Program accepts all
positional and named arguments and passes them forward to Run Process along with the name of the command to execute.
Named-only arguments
Starting from Robot Framework 3.1, keywords can accept argument that must always be named using the named argument syntax. If, for example, a keyword
would accept a single named-only argument example, it would always need to be used like example=value and using just value would not work. This syntax is
inspired by the keyword-only arguments syntax supported by Python 3.
For most parts named-only arguments work the same way as named arguments. The main difference is that libraries implemented with Python 2 using the static
library API do not support this syntax.
As an example of using the named-only arguments with user keywords, here is a variation of the Run Program in the above free named argument examples that
only supports configuring shell:
A totally different approach to specify arguments is embedding them into keyword names. This syntax is supported by both test library keywords and user
keywords.
2.2.3 Failures
A test case fails if any of the keyword it uses fails. Normally this means that execution of that test case is stopped, possible test teardown is executed, and then
execution continues from the next test case. It is also possible to use special continuable failures if stopping test execution is not desired.
Error messages
The error message assigned to a failed test case is got directly from the failed keyword. Often the error message is created by the keyword itself, but some
keywords allow configuring them.
In some circumstances, for example when continuable failures are used, a test case can fail multiple times. In that case the final error message is got by
combining the individual errors. Very long error messages are automatically cut from the middle to keep reports easier to read, but full error messages are
always visible in log files as messages of the failed keywords.
By default error messages are normal text, but they can contain HTML formatting. This is enabled by starting the error message with marker string *HTML*.
This marker will be removed from the final error message shown in reports and logs. Using HTML in a custom message is shown in the second example below.
HTML Error
${number} = Get Number
Should Be Equal ${number} 42 *HTML* Number is not my <b>MAGIC</b> number.
The [Documentation] setting allows you to set a free documentation for a test case. That text is shown in the command line output, as well as the resulting test
logs and test reports. It is possible to use simple HTML formatting in documentation and variables can be used to make the documentation dynamic.
If documentation is split into multiple columns, cells in one row are concatenated together with spaces. This is mainly be useful when using the HTML format
and columns are narrow. If documentation is split into multiple rows, the created documentation lines themselves are concatenated using newlines. Newlines
are not added if a line already ends with a newline or an escaping backslash.
Formatting
[Documentation] *This is bold*, _this is italic_ and here is a link: http://robotframework.org
No Operation
Variables
[Documentation] Executed at ${HOST} by ${USER}
No Operation
Splitting
[Documentation] This documentation is split into multiple columns
No Operation
Many lines
[Documentation] Here we have
... an automatic newline
No Operation
It is important that test cases have clear and descriptive names, and in that case they normally do not need any documentation. If the logic of the test case needs
documenting, it is often a sign that keywords in the test case need better names and they are to be enhanced, instead of adding extra documentation. Finally,
metadata, such as the environment and user information in the last example above, is often better specified using tags.
Tags are shown in test reports, logs and, of course, in the test data, so they provide metadata to test cases.
Statistics about test cases (total, passed, failed are automatically collected based on tags).
With tags, you can include or exclude test cases to be executed.
With tags, you can specify which test cases are considered critical.
In this section it is only explained how to set tags for test cases, and different ways to do it are listed below. These approaches can naturally be used together.
Tags are free text, but they are normalized so that they are converted to lowercase and all spaces are removed. If a test case gets the same tag several times,
other occurrences than the first one are removed. Tags can be created using variables, assuming that those variables exist.
Reserved tags
Users are generally free to use whatever tags that work in their context. There are, however, certain tags that have a predefined meaning for Robot Framework
itself, and using them for other purposes can have unexpected results. All special tags Robot Framework has and will have in the future have the robot: prefix.
To avoid problems, users should thus not use any tag with this prefixes unless actually activating the special functionality.
At the time of writing, the only special tags are robot:exit, that is automatically added to tests when stopping test execution gracefully, and robot:no-dry-
run, that can be used to disable the dry run mode. More usages are likely to be added in the future.
Setup and teardown are always a single keyword. If they need to take care of multiple separate tasks, it is possible to create higher-level user keywords for that
purpose. An alternative solution is executing multiple keywords using the BuiltIn keyword Run Keywords.
The test teardown is special in two ways. First of all, it is executed also when a test case fails, so it can be used for clean-up activities that must be done
regardless of the test case status. In addition, all the keywords in the teardown are also executed even if one of them fails. This continue on failure functionality
can be used also with normal keywords, but inside teardowns it is on by default.
The easiest way to specify a setup or a teardown for test cases in a test case file is using the Test Setup and Test Teardown settings in the Setting table. Individual
test cases can also have their own setup or teardown. They are defined with the [Setup] or [Teardown] settings in the test case table and they override possible
Test Setup and Test Teardown settings. Having no keyword after a [Setup] or [Teardown] setting means having no setup or teardown. It is also possible to use
value NONE to indicate that a test has no setup/teardown.
No teardown
[Documentation] Default setup, no teardown at all
Do Something
[Teardown]
No teardown 2
[Documentation] Setup and teardown can be disabled also with special value NONE
Do Something
[Teardown] NONE
Using variables
[Documentation] Setup and teardown specified using variables
[Setup] ${SETUP}
Do Something
[Teardown] ${TEARDOWN}
The name of the keyword to be executed as a setup or a teardown can be a variable. This facilitates having different setups or teardowns in different
environments by giving the keyword name as a variable from the command line.
Note
Test suites can have a setup and teardown of their own. A suite setup is executed before any test cases or sub test suites in that test suite, and similarly a suite teardown is
executed after them.
Template keywords can accept both normal positional and named arguments, as well as arguments embedded to the keyword name. Unlike with other settings,
it is not possible to define a template using a variable.
Basic usage
How a keyword accepting normal positional arguments can be used as a template is illustrated by the following example test cases. These two tests are
functionally fully identical.
As the example illustrates, it is possible to specify the template for an individual test case using the [Template] setting. An alternative approach is using the
Test Template setting in the Setting table, in which case the template is applied for all test cases in that test case file. The [Template] setting overrides the
possible template set in the Setting table, and an empty value for [Template] means that the test has no template even when Test Template is used. It is also
possible to use value NONE to indicate that a test has no template.
If a templated test case has multiple data rows in its body, the template is applied for all the rows one by one. This means that the same keyword is executed
multiple times, once with data on each row. Templated tests are also special so that all the rounds are executed even if one or more of them fails. It is possible to
use this kind of continue on failure mode with normal tests too, but with the templated tests the mode is on automatically.
Using arguments with default values or varargs, as well as using named arguments and free named arguments, work with templates exactly like they work
otherwise. Using variables in arguments is also supported normally.
Templates support a variation of the embedded argument syntax. With templates this syntax works so that if the template keyword has variables in its name,
they are considered placeholders for arguments and replaced with the actual arguments used with the template. The resulting keyword is then used without
positional arguments. This is best illustrated with an example:
When embedded arguments are used with templates, the number of arguments in the template keyword name must match the number of arguments it is used
with. The argument names do not need to match the arguments of the original keyword, though, and it is also possible to use different arguments altogether:
New arguments
[Template] The ${meaning} of ${life} should be 42
result 21 * 2
The main benefit of using embedded arguments with templates is that argument names are specified explicitly. When using normal arguments, the same effect
can be achieved by naming the columns that contain arguments. This is illustrated by the data-driven style example in the next section.
If templates are used with for loops, the template is applied for all the steps inside the loop. The continue on failure mode is in use also in this case, which
means that all the steps are executed with all the looped elements even if there are failures.
Keyword-driven style
Workflow tests, such as the Valid Login test described earlier, are constructed from several keywords and their possible arguments. Their normal structure is
that first the system is taken into the initial state (Open Login Page in the Valid Login example), then something is done to the system (Input Name, Input
Password, Submit Credentials), and finally it is verified that the system behaved as expected (Welcome Page Should Be Open).
Data-driven style
Another style to write test cases is the data-driven approach where test cases use only one higher-level keyword, often created as a user keyword, that hides the
actual test workflow. These tests are very useful when there is a need to test the same scenario with different input and/or output data. It would be possible to
repeat the same keyword with every test, but the test template functionality allows specifying the keyword to use only once.
Tip
Naming columns like in the example above makes tests easier to understand. This is possible because on the header row other cells except the first one are ignored.
The above example has six separate tests, one for each invalid user/password combination, and the example below illustrates how to have only one test with all
the combinations. When using test templates, all the rounds in a test are executed even if there are failures, so there is no real functional difference between
these two styles. In the above example separate combinations are named so it is easier to see what they test, but having potentially large number of these tests
may mess-up statistics. Which style to use depends on the context and personal preferences.
Behavior-driven style
It is also possible to write test cases as requirements that also non-technical project stakeholders must understand. These executable requirements are a corner
stone of a process commonly called Acceptance Test Driven Development (ATDD) or Specification by Example.
One way to write these requirements/tests is Given-When-Then style popularized by Behavior Driven Development (BDD). When writing test cases in this
style, the initial state is usually expressed with a keyword starting with word Given, the actions are described with keyword starting with When and the
expectations with a keyword starting with Then. Keyword starting with And or But may be used if a step has more than one action.
Prefixes Given, When, Then, And and But are dropped when matching keywords are searched, if no match with the full name is found. This works for both user
keywords and library keywords. For example, Given login page is open in the above example can be implemented as user keyword either with or without the
word Given. Ignoring prefixes also allows using the same keyword with different prefixes. For example Welcome page should be open could also used as And
welcome page should be open.
When writing concrete examples it is useful to be able pass actual data to keyword implementations. User keywords support this by allowing embedding
arguments into keyword name.
The following settings in the Setting table can be used to customize the test suite:
Documentation
Used for specifying a test suite documentation
Metadata
Used for setting free test suite metadata as name-value pairs.
Suite Setup, Suite Teardown
Specify suite setup and teardown.
Note
All setting names can optionally include a colon at the end, for example Documentation:. This can make reading the settings easier especially when using the plain text
format.
Note
Setting names are case-insensitive, but the format used above is recommended. Prior to Robot Framework 3.1, settings were also space-insensitive meaning that spaces
could be removed (e.g. SuiteSetup) or extra spaces added (e.g. M e t a d a t a). This is now deprecated and only the format above, case-insensitively, is supported.
When a test directory is executed, the files and directories it contains are processed recursively as follows:
Files and directories with names starting with a dot (.) or an underscore (_) are ignored.
Directories with the name CVS are ignored (case-sensitive).
Files in supported file formats are processed.
Other files are ignored.
If a file or directory that is processed does not contain any test cases, it is silently ignored (a message is written to the syslog) and the processing continues.
Initialization files
A test suite created from a directory can have similar settings as a suite created from a test case file. Because a directory alone cannot have that kind of
information, it must be placed into a special test suite initialization file. An initialization file name must always be of the format __init__.ext, where the
extension must be one of the supported file formats (typically __init__.robot). The name format is borrowed from Python, where files named in this manner
denote that a directory is a module.
Initialization files have the same structure and syntax as test case files, except that they cannot have test case tables and not all settings are supported. Variables
and keywords created or imported in initialization files are not available in the lower level test suites. If you need to share variables or keywords, you can put
them into resource files that can be imported both by initialization and test case files.
The main usage for initialization files is specifying test suite related settings similarly as in test case files, but setting some test case related settings is also
possible. How to use different settings in the initialization files is explained below.
The file or directory name can contain a prefix to control the execution order of the suites. The prefix is separated from the base name by two underscores and,
when constructing the actual test suite name, both the prefix and underscores are removed. For example files 01__some_tests.robot and 02__more_tests.robot
create test suites Some Tests and More Tests, respectively, and the former is executed before the latter.
The documentation for a test suite is set using the Documentation setting in the Setting table. It can be used in test case files or, with higher-level suites, in test
suite initialization files. Test suite documentation has exactly the same characteristics regarding to where it is shown and how it can be created as test case
documentation.
Both the name and documentation of the top-level test suite can be overridden in test execution. This can be done with the command line options --name and
--doc, respectively, as explained in section Setting metadata.
The name and value for the metadata are located in the columns following Metadata. The value is handled similarly as documentation, which means that it can
be split into several cells (joined together with spaces) or into several rows (joined together with newlines), simple HTML formatting works and even variables
can be used.
For top-level test suites, it is possible to set metadata also with the --metadata command line option. This is discussed in more detail in section Setting
metadata.
Similarly as with test cases, a suite setup and teardown are keywords that may take arguments. They are defined in the Setting table with Suite Setup and
Suite Teardown settings, respectively. Keyword names and possible arguments are located in the columns after the setting name.
If a suite setup fails, all test cases in it and its child test suites are immediately assigned a fail status and they are not actually executed. This makes suite setups
ideal for checking preconditions that must be met before running test cases is possible.
A suite teardown is normally used for cleaning up after all the test cases have been executed. It is executed even if the setup of the same suite fails. If the suite
teardown fails, all test cases in the suite are marked failed, regardless of their original execution status. Note that all the keywords in suite teardowns are
executed even if one of them fails.
The name of the keyword to be executed as a setup or a teardown can be a variable. This facilitates having different setups or teardowns in different
environments by giving the keyword name as a variable from the command line.
Test libraries are normally imported using the Library setting in the Setting table and having the library name in the subsequent column. Unlike most of the
other data, the library name is both case- and space-sensitive. If a library is in a package, the full name including the package name must be used.
In those cases where the library needs arguments, they are listed in the columns after the library name. It is possible to use default values, variable number of
arguments, and named arguments in test library imports similarly as with arguments to keywords. Both the library name and arguments can be set using
variables.
It is possible to import test libraries in test case files, resource files and test suite initialization files. In all these cases, all the keywords in the imported library
are available in that file. With resource files, those keywords are also available in other files using them.
Another possibility to take a test library into use is using the keyword Import Library from the BuiltIn library. This keyword takes the library name and possible
arguments similarly as the Library setting. Keywords from the imported library are available in the test suite where the Import Library keyword was used. This
approach is useful in cases where the library is not available when the test execution starts and only some other keywords make it available.
The most common way to specify a test library to import is using its name, like it has been done in all the examples in this section. In these cases Robot
Framework tries to find the class or module implementing the library from the module search path. Libraries that are installed somehow ought to be in the
module search path automatically, but with other libraries the search path may need to be configured separately.
The biggest benefit of this approach is that when the module search path has been configured, often using a custom start-up script, normal users do not need to
think where libraries actually are installed. The drawback is that getting your own, possible very simple, libraries into the search path may require some
additional configuration.
Another mechanism for specifying the library to import is using a path to it in the file system. This path is considered relative to the directory where current test
data file is situated similarly as paths to resource and variable files. The main benefit of this approach is that there is no need to configure the module search
path.
If the library is a file, the path to it must contain extension. For Python libraries the extension is naturally .py and for Java libraries it can either be .class or
.java, but the class file must always be available. If Python library is implemented as a directory, the path to it must have a trailing forward slash (/) if the path
is relative. With absolute paths the trailing slash is optional. Following examples demonstrate these different usages.
A limitation of this approach is that libraries implemented as Python classes must be in a module with the same name as the class. Additionally, importing
libraries distributed in JAR or ZIP packages is not possible with this mechanism.
There is a need to import the same library several times with different arguments. This is not possible otherwise.
The library name is inconveniently long. This can happen, for example, if a Java library has a long package name.
You want to use variables to import different libraries in different environments, but refer to them with the same name.
The library name is misleading or otherwise poor. In this case, changing the actual name is, of course, a better solution.
The basic syntax for specifying the new name is having the text WITH NAME (case-sensitive) after the library name and then having the new name in the next
cell. The specified name is shown in logs and must be used in the test data when using keywords' full name (LibraryName.Keyword Name).
Possible arguments to the library are placed into cells between the original library name and the WITH NAME text. The following example illustrates how the
same library can be imported several times with different arguments:
Setting a custom name to a test library works both when importing a library in the Setting table and when using the Import Library keyword.
BuiltIn
Collections
DateTime
Dialogs
OperatingSystem
Process
Screenshot
String
Telnet
XML
Remote library
In addition to the normal standard libraries listed above, there is also Remote library that is totally different than the other standard libraries. It does not have
any keywords of its own but it works as a proxy between Robot Framework and actual test library implementations. These libraries can be running on other
machines than the core framework and can even be implemented using languages not supported by Robot Framework natively.
See separate Remote library interface section for more information about this concept.
Generic and custom libraries can obviously also be implemented by teams using Robot Framework. See Creating test libraries section for more information
about that topic.
Different external libraries can have a totally different mechanism for installing them and taking them into use. Sometimes they may also require some other
dependencies to be installed separately. All libraries should have clear installation and usage documentation and they should preferably automate the
installation process.
2.6 Variables
2.6.1 Introduction
2.6.2 Using variables
Scalar variable syntax
List variable syntax
Dictionary variable syntax
Accessing list and dictionary items
Environment variables
Java system properties
2.6.3 Creating variables
Variable table
Variable file
Setting variables in command line
Return values from keywords
Using Set Test/Suite/Global Variable keywords
2.6.4 Built-in variables
Operating-system variables
Number variables
Boolean and None/null variables
Space and empty variables
Automatic variables
2.6.5 Variable priorities and scopes
Variable priorities
Variable scopes
2.6.6 Advanced variable features
Extended variable syntax
Extended variable assignment
Variables inside variables
2.6.1 Introduction
Variables are an integral feature of Robot Framework, and they can be used in most places in test data. Most commonly, they are used in arguments for
keywords in test case tables and keyword tables, but also all settings allow variables in their values. A normal keyword name cannot be specified with a
variable, but the BuiltIn keyword Run Keyword can be used to get the same effect.
Robot Framework has its own variables that can be used as scalars, lists or dictionaries using syntax ${SCALAR}, @{LIST} and &{DICT}, respectively. In addition
to this, environment variables can be used directly with syntax %{ENV_VAR}.
When strings change often in the test data. With variables you only need to make these changes in one place.
When creating system-independent and operating-system-independent test data. Using variables instead of hard-coded strings eases that considerably (for
example, ${RESOURCES} instead of c:\resources, or ${HOST} instead of 10.0.0.1:8080). Because variables can be set from the command line when
tests are started, changing system-specific variables is easy (for example, --variable HOST:10.0.0.2:1234 --variable
RESOURCES:/opt/resources). This also facilitates localization testing, which often involves running the same tests with different strings.
When there is a need to have objects other than strings as arguments for keywords. This is not possible without variables.
When different keywords, even in different test libraries, need to communicate. You can assign a return value from one keyword to a variable and pass it
as an argument to another.
When values in the test data are long or otherwise complicated. For example, ${URL} is shorter than
http://long.domain.name:8080/path/to/service?foo=1&bar=2&zap=42.
If a non-existent variable is used in the test data, the keyword using it fails. If the same syntax that is used for variables is needed as a literal string, it must be
escaped with a backslash as in \${NAME}.
Robot Framework variables, similarly as keywords, are case-insensitive, and also spaces and underscores are ignored. However, it is recommended to use
capital letters with global variables (for example, ${PATH} or ${TWO WORDS}) and small letters with local variables that are only available in certain test cases or
user keywords (for example, ${my var}). Much more importantly, though, case should be used consistently.
Variable name consists of the variable type identifier ($, @, &, %), curly braces ({, }) and the actual variable name between the braces. Unlike in some
programming languages where similar variable syntax is used, curly braces are always mandatory. Variable names can basically have any characters between
the curly braces. However, using only alphabetic characters from a to z, numbers, underscore and space is recommended, and it is even a requirement for using
the extended variable syntax.
The most common way to use variables in Robot Framework test data is using the scalar variable syntax like ${var}. When this syntax is used, the variable
name is replaced with its value as-is. Most of the time variable values are strings, but variables can contain any object, including numbers, lists, dictionaries, or
even custom objects.
The example below illustrates the usage of scalar variables. Assuming that the variables ${GREET} and ${NAME} are available and assigned to strings Hello and
world, respectively, both the example test cases are equivalent.
Variables
Log ${GREET}
Log ${GREET}, ${NAME}!!
When a scalar variable is used alone without any text or other variables around it, like in ${GREET} above, the variable is replaced with its value as-is and the
value can be any object. If the variable is not used alone, like ${GREER}, ${NAME}!! above, its value is first converted into a string and then concatenated with
the other data.
Note
Variable values are used as-is without conversions also when passing arguments to keywords using the named arguments syntax like argname=${var}.
The example below demonstrates the difference between having a variable in alone or with other content. First, let us assume that we have a variable ${STR} set
to a string Hello, world! and ${OBJ} set to an instance of the following Java object:
Finally, when this test data is executed, different keywords receive the arguments as explained below:
Note
Converting variables to Unicode obviously fails if the variable cannot be represented as Unicode. This can happen, for example, if you try to use byte sequences as
arguments to keywords so that you catenate the values together like ${byte1}${byte2}. A workaround is creating a variable that contains the whole value and using it
alone in the cell (e.g. ${bytes}) because then the value is used as-is.
When a variable is used as a scalar like ${EXAMPLE}, its value is be used as-is. If a variable value is a list or list-like, it is also possible to use it as a list variable
like @{EXAMPLE}. In this case individual list items are passed in as arguments separately. This is easiest to explain with an example. Assuming that a variable
@{USER} has value ['robot', 'secret'], the following two test cases are equivalent:
List Variable
Login @{USER}
Robot Framework stores its own variables in one internal storage and allows using them as scalars, lists or dictionaries. Using a variable as a list requires its
value to be a Python list or list-like object. Robot Framework does not allow strings to be used as lists, but other iterable objects such as tuples or dictionaries
are accepted.
Prior to Robot Framework 2.9, scalar and list variables were stored separately, but it was possible to use list variables as scalars and scalar variables as lists.
This caused lot of confusion when there accidentally was a scalar variable and a list variable with same name but different value.
It is possible to use list variables with other arguments, including other list variables.
List variables can be used only with some of the settings. They can be used in arguments to imported libraries and variable files, but library and variable file
names themselves cannot be list variables. Also with setups and teardowns list variable can not be used as the name of the keyword, but can be used in
arguments. With tag related settings they can be used freely. Using scalar variables is possible in those places where list variables are not supported.
As discussed above, a variable containing a list can be used as a list variable to pass list items to a keyword as individual arguments. Similarly a variable
containing a Python dictionary or a dictionary-like object can be used as a dictionary variable like &{EXAMPLE}. In practice this means that individual items of
the dictionary are passed as named arguments to the keyword. Assuming that a variable &{USER} has value {'name': 'robot', 'password': 'secret'}, the
following two test cases are equivalent.
*** Test Cases ***
Constants
Login name=robot password=secret
Dict Variable
Login &{USER}
It is possible to use dictionary variables with other arguments, including other dictionary variables. Because named argument syntax requires positional
arguments to be before named argument, dictionaries can only be followed by named arguments or other dictionaries.
Dictionary variables cannot generally be used with settings. The only exception are imports, setups and teardowns where dictionaries can be used as arguments.
It is possible to access items of lists and dictionaries using special syntax ${var}[item]. Accessing items is an old feature, but prior to Robot Framework 3.1
the syntax was @{var}[item] with lists and &{var}[item] with dictionaries. The old syntax still works in Robot Framework 3.1, but it will be deprecated in
Robot Framework 3.2 and its meaning will change in Robot Framework 3.3.
It is possible to access a certain item of a list variable with the syntax ${var}[index], where index is the index of the selected value. Indices start from zero,
negative indices can be used to access items from the end, and trying to access an item with too large an index causes an error. Indices are automatically
converted to integers, and it is also possible to use variables as indices. List items accessed in this manner can be used similarly as scalar variables.
Negative index
Log ${LIST}[-1]
List item access supports also the same "slice" functionality as Python with syntax like ${var}[1:]. With this syntax you do not get a single item but a slice of
the original list. Same way as with Python you can specify the start index, the end index, and the step:
End index
Keyword ${LIST}[:4]
Step
Keyword ${LIST}[::2]
Keyword ${LIST}[2:-1:2]
Note
The slice syntax is new in Robot Framework 3.1 and does not work with the old @{var}[index] syntax.
It is possible to access a certain value of a dictionary variable with the syntax ${NAME}[key], where key is the name of the selected value. Keys are considered
to be strings, but non-strings keys can be used as variables. Dictionary values accessed in this manner can be used similarly as scalar variables.
If a key is a string, it is possible to access its value also using attribute access syntax ${NAME.key}. See Creating dictionary variables for more details about this
syntax.
Attribute access
Login ${USER.name} ${USER.password}
Title Should Be Welcome ${USER.name}!
Also nested list and dictionary structures can be accessed using the same item access syntax like ${var}[item1][item2]. This is especially useful when
working with JSON data often returned by REST services. For example, if a variable ${DATA} contains [{'id': 1, 'name': 'Robot'}, {'id': 2, 'name':
'Mr. X'}], this tests would pass:
Environment variables
Robot Framework allows using environment variables in the test data using the syntax %{ENV_VAR_NAME}. They are limited to string values.
Environment variables set in the operating system before the test execution are available during it, and it is possible to create new ones with the keyword Set
Environment Variable or delete existing ones with the keyword Delete Environment Variable, both available in the OperatingSystem library. Because
environment variables are global, environment variables set in one test case can be used in other test cases executed after it. However, changes to environment
variables are not effective after the test execution.
When running tests with Jython, it is possible to access Java system properties using same syntax as environment variables. If an environment variable and a
system property with same name exist, the environment variable will be used.
Variable table
The most common source for variables are Variable tables in test case files and resource files. Variable tables are convenient, because they allow creating
variables in the same place as the rest of the test data, and the needed syntax is very simple. Their main disadvantages are that values are always strings and
they cannot be created dynamically. If either of these is a problem, variable files can be used instead.
The simplest possible variable assignment is setting a string into a scalar variable. This is done by giving the variable name (including ${}) in the first column
of the Variable table and the value in the second one. If the second column is empty, an empty string is set as a value. Also an already defined variable can be
used in the value.
If a scalar variable has a long value, it can be split to multiple columns and rows. By default cells are catenated together using a space, but this can be changed
by having SEPARATOR=<sep> in the first cell.
Joining long values like above is a new feature in Robot Framework 2.9.
Creating list variables is as easy as creating scalar variables. Again, the variable name is in the first column of the Variable table and values in the subsequent
columns. A list variable can have any number of values, starting from zero, and if many values are needed, they can be split into several rows.
Dictionary variables can be created in the variable table similarly as list variables. The difference is that items need to be created using name=value syntax or
existing dictionary variables. If there are multiple items with same name, the last value has precedence. If a name contains a literal equal sign, it can be escaped
with a backslash like \=.
Dictionary variables have two extra properties compared to normal Python dictionaries. First of all, values of these dictionaries can be accessed like attributes,
which means that it is possible to use extended variable syntax like ${VAR.key}. This only works if the key is a valid attribute name and does not match any
normal attribute Python dictionaries have. For example, individual value &{USER}[name] can also be accessed like ${USER.name} (notice that $ is needed in
this context), but using ${MANY.3} is not possible.
Note
Starting from Robot Framework 3.0.3, dictionary variable keys are accessible recursively like ${VAR.nested.key}. This eases working with nested data structures.
Another special property of dictionary variables is that they are ordered. This means that if these dictionaries are iterated, their items always come in the order
they are defined. This can be useful if dictionaries are used as list variables with for loops or otherwise. When a dictionary is used as a list variable, the actual
value contains dictionary keys. For example, @{MANY} variable would have value ['first', 'second', 3].
Variable file
Variable files are the most powerful mechanism for creating different kind of variables. It is possible to assign variables to any object using them, and they also
enable creating variables dynamically. The variable file syntax and taking variable files into use is explained in section Resource and variable files.
Variables can be set from the command line either individually with the --variable (-v) option or using a variable file with the --variablefile (-V)
option. Variables set from the command line are globally available for all executed test data files, and they also override possible variables with the same names
in the Variable table and in variable files imported in the test data.
The syntax for setting individual variables is --variable name:value, where name is the name of the variable without ${} and value is its value. Several
variables can be set by using this option several times. Only scalar variables can be set using this syntax and they can only get string values.
--variable EXAMPLE:value
--variable HOST:localhost:7272 --variable USER:robot
The basic syntax for taking variable files into use from the command line is --variablefile path/to/variables.py, and Taking variable files into use
section has more details. What variables actually are created depends on what variables there are in the referenced variable file.
If both variable files and individual variables are given from the command line, the latter have higher priority.
Return values from keywords can also be set into variables. This allows communication between different keywords even in different test libraries.
Variables set in this manner are otherwise similar to any other variables, but they are available only in the local scope where they are created. Thus it is not
possible, for example, to set a variable like this in one test case and use it in another. This is because, in general, automated test cases should not depend on each
other, and accidentally setting a variable that is used elsewhere could cause hard-to-debug errors. If there is a genuine need for setting a variable in one test case
and using it in another, it is possible to use BuiltIn keywords as explained in the next section.
Any value returned by a keyword can be assigned to a scalar variable. As illustrated by the example below, the required syntax is very simple:
In the above example the value returned by the Get X keyword is first set into the variable ${x} and then used by the Log keyword. Having the equals sign =
after the variable name is not obligatory, but it makes the assignment more explicit. Creating local variables like this works both in test case and user keyword
level.
Notice that although a value is assigned to a scalar variable, it can be used as a list variable if it has a list-like value and as a dictionary variable if it has a
dictionary-like value.
If a keyword returns a list or any list-like object, it is possible to assign it to a list variable:
Because all Robot Framework variables are stored in the same namespace, there is not much difference between assigning a value to a scalar variable or a list
variable. This can be seen by comparing the last two examples above. The main differences are that when creating a list variable, Robot Framework
automatically verifies that the value is a list or list-like, and the stored variable value will be a new list created from the return value. When assigning to a scalar
variable, the return value is not verified and the stored value will be the exact same object that was returned.
If a keyword returns a dictionary or any dictionary-like object, it is possible to assign it to a dictionary variable:
Because all Robot Framework variables are stored in the same namespace, it would also be possible to assign a dictionary into a scalar variable and use it later
as a dictionary when needed. There are, however, some actual benefits in creating a dictionary variable explicitly. First of all, Robot Framework verifies that the
returned value is a dictionary or dictionary-like similarly as it verifies that list variables can only get a list-like value.
A bigger benefit is that the value is converted into a special dictionary that it uses also when creating dictionary variables in the variable table. Values in these
dictionaries can be accessed using attribute access like ${dict.first} in the above example. These dictionaries are also ordered, but if the original dictionary
was not ordered, the resulting order is arbitrary.
Assigning multiple variables
If a keyword returns a list or a list-like object, it is possible to assign individual values into multiple scalar variables or into scalar variables and a list variable.
Assuming that the keyword Get Three returns a list [1, 2, 3], the following variables are created:
It is an error if the returned list has more or less values than there are scalar variables to assign. Additionally, only one list variable is allowed and dictionary
variables can only be assigned alone.
The support for assigning multiple variables was slightly changed in Robot Framework 2.9. Prior to it a list variable was only allowed as the last assigned
variable, but nowadays it can be used anywhere. Additionally, it was possible to return more values than scalar variables. In that case the last scalar variable
was magically turned into a list containing the extra values.
The BuiltIn library has keywords Set Test Variable, Set Suite Variable and Set Global Variable which can be used for setting variables dynamically during the
test execution. If a variable already exists within the new scope, its value will be overwritten, and otherwise a new variable is created.
Variables set with Set Test Variable keyword are available everywhere within the scope of the currently executed test case. For example, if you set a variable in
a user keyword, it is available both in the test case level and also in all other user keywords used in the current test. Other test cases will not see variables set
with this keyword.
Variables set with Set Suite Variable keyword are available everywhere within the scope of the currently executed test suite. Setting variables with this keyword
thus has the same effect as creating them using the Variable table in the test data file or importing them from variable files. Other test suites, including possible
child test suites, will not see variables set with this keyword.
Variables set with Set Global Variable keyword are globally available in all test cases and suites executed after setting them. Setting variables with this keyword
thus has the same effect as creating from the command line using the options --variable or --variablefile. Because this keyword can change variables
everywhere, it should be used with care.
Note
Set Test/Suite/Global Variable keywords set named variables directly into test, suite or global variable scope and return nothing. On the other hand, another BuiltIn
keyword Set Variable sets local variables using return values.
Operating-system variables
Built-in variables related to the operating system ease making the test data operating-system-agnostic.
The variable syntax can be used for creating both integers and floating point numbers, as illustrated in the example below. This is useful when a keyword
expects to get an actual number, and not a string that just looks like a number, as an argument.
Example 1B
Connect example.com ${80} # Connect gets a string and an integer
Example 2
Do X ${3.14} ${-1e-4} # Do X gets floating point numbers 3.14 and -0.0001
It is possible to create integers also from binary, octal, and hexadecimal values using 0b, 0o and 0x prefixes, respectively. The syntax is case insensitive.
Also Boolean values and Python None and Java null can be created using the variable syntax similarly as numbers.
None
Do XYZ ${None} # Do XYZ gets Python None as an argument
Null
${ret} = Get Value arg # Checking that Get Value returns Java null
Should Be Equal ${ret} ${null}
These variables are case-insensitive, so for example ${True} and ${true} are equivalent. Additionally, ${None} and ${null} are synonyms, because when
running tests on the Jython interpreter, Jython automatically converts None and null to the correct format when necessary.
It is possible to create spaces and empty strings using variables ${SPACE} and ${EMPTY}, respectively. These variables are useful, for example, when there
would otherwise be a need to escape spaces or empty cells with a backslash. If more than one space is needed, it is possible to use the extended variable syntax
like ${SPACE * 5}. In the following example, Should Be Equal keyword gets identical arguments but those using variables are easier to understand than those
using backslashes.
Four spaces
Should Be Equal ${SPACE * 4} \ \ \ \ \
Ten spaces
Should Be Equal ${SPACE * 10} \ \ \ \ \ \ \ \ \ \ \
Quoted space
Should Be Equal "${SPACE}" " "
Quoted spaces
Should Be Equal "${SPACE * 2}" " \ "
Empty
Should Be Equal ${EMPTY} \
There is also an empty list variable @{EMPTY} and an empty dictionary variable &{EMPTY}. Because they have no content, they basically vanish when used
somewhere in the test data. They are useful, for example, with test templates when the template keyword is used without arguments or when overriding list or
dictionary variables in different scopes. Modifying the value of @{EMPTY} or &{EMPTY} is not possible.
Override
Set Global Variable @{LIST} @{EMPTY}
Set Suite Variable &{DICT} &{EMPTY}
Note
${SPACE} represents the ASCII space (\x20) and other spaces should be specified using the escape sequences like \xA0 (NO-BREAK SPACE) and \u3000
(IDEOGRAPHIC SPACE).
Note
Automatic variables
Some automatic variables can also be used in the test data. These variables can have different values during the test execution and some of them are not even
available all the time. Altering the value of these variables does not affect the original values, but some values can be changed dynamically using keywords
from the BuiltIn library.
Suite related variables ${SUITE SOURCE}, ${SUITE NAME}, ${SUITE DOCUMENTATION} and &{SUITE METADATA} are available already when test libraries and
variable files are imported. Possible variables in these automatic variables are not yet resolved at the import time, though.
Variable priorities
Variables set in the command line have the highest priority of all variables that can be set before the actual test execution starts. They override
possible variables created in Variable tables in test case files, as well as in resource and variable files imported in the test data.
Individually set variables (--variable option) override the variables set using variable files (--variablefile option). If you specify same
individual variable multiple times, the one specified last will override earlier ones. This allows setting default values for variables in a start-up
script and overriding them from the command line. Notice, though, that if multiple variable files have same variables, the ones in the file specified
first have the highest priority.
Variable table in a test case file
Variables created using the Variable table in a test case file are available for all the test cases in that file. These variables override possible variables
with same names in imported resource and variable files.
Variables created in the variable tables are available in all other tables in the file where they are created. This means that they can be used also in
the Setting table, for example, for importing more variables from resource and variable files.
Variables imported from the resource and variable files have the lowest priority of all variables created in the test data. Variables from resource files
and variable files have the same priority. If several resource and/or variable file have same variables, the ones in the file imported first are taken into
use.
If a resource file imports resource files or variable files, variables in its own Variable table have a higher priority than variables it imports. All these
variables are available for files that import this resource file.
Note that variables imported from resource and variable files are not available in the Variable table of the file that imports them. This is due to the
Variable table being processed before the Setting table where the resource files and variable files are imported.
Variables set during the test execution either using return values from keywords or using Set Test/Suite/Global Variable keywords always override
possible existing variables in the scope where they are set. In a sense they thus have the highest priority, but on the other hand they do not affect
variables outside the scope they are defined.
Built-in variables
Built-in variables like ${TEMPDIR} and ${TEST_NAME} have the highest priority of all variables. They cannot be overridden using Variable table or
from command line, but even they can be reset during the test execution. An exception to this rule are number variables, which are resolved
dynamically if no variable is found otherwise. They can thus be overridden, but that is generally a bad idea. Additionally ${CURDIR} is special
because it is replaced already during the test data processing time.
Variable scopes
Depending on where and how they are created, variables can have a global, test suite, test case or local scope.
Global scope
Global variables are available everywhere in the test data. These variables are normally set from the command line with the --variable and --variablefile
options, but it is also possible to create new global variables or change the existing ones with the BuiltIn keyword Set Global Variable anywhere in the test data.
Additionally also built-in variables are global.
Variables with the test suite scope are available anywhere in the test suite where they are defined or imported. They can be created in Variable tables, imported
from resource and variable files, or set during the test execution using the BuiltIn keyword Set Suite Variable.
The test suite scope is not recursive, which means that variables available in a higher-level test suite are not available in lower-level suites. If necessary,
resource and variable files can be used for sharing variables.
Since these variables can be considered global in the test suite where they are used, it is recommended to use capital letters also with them.
Variables with the test case scope are visible in a test case and in all user keywords the test uses. Initially there are no variables in this scope, but it is possible to
create them by using the BuiltIn keyword Set Test Variable anywhere in a test case.
Also variables in the test case scope are to some extend global. It is thus generally recommended to use capital letters with them too.
Local scope
Test cases and user keywords have a local variable scope that is not seen by other tests or keywords. Local variables can be created using return values from
executed keywords and user keywords also get them as arguments.
Note
Prior to Robot Framework 2.9 variables in the local scope leaked to lower level user keywords. This was never an intended feature, and variables should be set or passed
explicitly also with earlier versions.
Extended variable syntax allows accessing attributes of an object assigned to a variable (for example, ${object.attribute}) and even calling its methods (for
example, ${obj.getName()}). It works both with scalar and list variables, but is mainly useful with the former
Extended variable syntax is a powerful feature, but it should be used with care. Accessing attributes is normally not a problem, on the contrary, because one
variable containing an object with several attributes is often better than having several variables. On the other hand, calling methods, especially when they are
used with arguments, can make the test data pretty complicated to understand. If that happens, it is recommended to move the code into a test library.
The most common usages of extended variable syntax are illustrated in the example below. First assume that we have the following variable file and test case:
class MyObject:
def __str__(self):
return self.name
OBJECT = MyObject('Robot')
DICTIONARY = {1: 'one', 2: 'two', 3: 'three'}
When this test data is executed, the keywords get the arguments as explained below:
1. The variable is searched using the full variable name. The extended variable syntax is evaluated only if no matching variable is found.
2. The name of the base variable is created. The body of the name consists of all the characters after the opening { until the first occurrence of a character
that is not an alphanumeric character or a space. For example, base variables of ${OBJECT.name} and ${DICTIONARY[2]}) are OBJECT and DICTIONARY,
respectively.
3. A variable matching the body is searched. If there is no match, an exception is raised and the test case fails.
4. The expression inside the curly brackets is evaluated as a Python expression, so that the base variable name is replaced with its value. If the evaluation
fails because of an invalid syntax or that the queried attribute does not exist, an exception is raised and the test fails.
5. The whole extended variable is replaced with the value returned from the evaluation.
If the object that is used is implemented with Java, the extended variable syntax allows you to access attributes using so-called bean properties. In essence, this
means that if you have an object with the getName method set into a variable ${OBJ}, then the syntax ${OBJ.name} is equivalent to but clearer than
${OBJ.getName()}. The Python object used in the previous example could thus be replaced with the following Java implementation:
Number
${number} = Set Variable ${-2}
Log ${number * 10} # Logs -20
Log ${number.__abs__()} # Logs 2
Note that even though abs(number) is recommended over number.__abs__() in normal Python code, using ${abs(number)} does not work. This is because
the variable name must be in the beginning of the extended syntax. Using __xxx__ methods in the test data like this is already a bit questionable, and it is
normally better to move this kind of logic into test libraries.
Extended variable syntax works also in list variable context. If, for example, an object assigned to a variable ${EXTENDED} has an attribute attribute that
contains a list as a value, it can be used as a list variable @{EXTENDED.attribute}.
It is possible to set attributes of objects stored to scalar variables using keyword return values and a variation of the extended variable syntax. Assuming we
have variable ${OBJECT} from the previous examples, attributes could be set to it like in the example below.
The extended variable assignment syntax is evaluated using the following rules:
1. The assigned variable must be a scalar variable and have at least one dot. Otherwise the extended assignment syntax is not used and the variable is
assigned normally.
2. If there exists a variable with the full name (e.g. ${OBJECT.name} in the example above) that variable will be assigned a new value and the extended
syntax is not used.
3. The name of the base variable is created. The body of the name consists of all the characters between the opening ${ and the last dot, for example,
OBJECT in ${OBJECT.name} and foo.bar in ${foo.bar.zap}. As the second example illustrates, the base name may contain normal extended variable
syntax.
4. The name of the attribute to set is created by taking all the characters between the last dot and the closing }, for example, name in ${OBJECT.name}. If the
name does not start with a letter or underscore and contain only these characters and numbers, the attribute is considered invalid and the extended syntax
is not used. A new variable with the full name is created instead.
5. A variable matching the base name is searched. If no variable is found, the extended syntax is not used and, instead, a new variable is created using the
full variable name.
6. If the found variable is a string or a number, the extended syntax is ignored and a new variable created using the full name. This is done because you
cannot add new attributes to Python strings or numbers, and this way the new syntax is also less backwards-incompatible.
7. If all the previous rules match, the attribute is set to the base variable. If setting fails for any reason, an exception is raised and the test fails.
Note
Unlike when assigning variables normally using return values from keywords, changes to variables done using the extended assign syntax are not limited to the current
scope. Because no new variable is created but instead the state of an existing variable is changed, all tests and keywords that see that variable will also see the changes.
Variables are allowed also inside variables, and when this syntax is used, variables are resolved from the inside out. For example, if you have a variable
${var${x}}, then ${x} is resolved first. If it has the value name, the final value is then the value of the variable ${varname}. There can be several nested
variables, but resolving the outermost fails, if any of them does not exist.
In the example below, Do X gets the value ${JOHN HOME} or ${JANE HOME}, depending on if Get Name returns john or jane. If it returns something else,
resolving ${${name} HOME} fails.
Basic syntax
In many ways, the overall user keyword syntax is identical to the test case syntax. User keywords are created in keyword tables which differ from test case
tables only by the name that is used to identify them. User keyword names are in the first column similarly as test cases names. Also user keywords are created
from keywords, either from keywords in test libraries or other user keywords. Keyword names are normally in the second column, but when setting variables
from keyword return values, they are in the subsequent columns.
Most user keywords take some arguments. This important feature is used already in the second example above, and it is explained in detail later in this section,
similarly as user keyword return values.
User keywords can be created in test case files, resource files, and test suite initialization files. Keywords created in resource files are available for files using
them, whereas other keywords are only available in the files where they are created.
User keywords can have similar settings as test cases, and they have the same square bracket syntax separating them from keyword names. All available
settings are listed below and explained later in this section.
[Documentation]
Used for setting a user keyword documentation.
[Tags]
Sets tags for the keyword.
[Arguments]
Specifies user keyword arguments.
[Return]
Specifies user keyword return values.
[Teardown]
Specify user keyword teardown.
[Timeout]
Sets the possible user keyword timeout. Timeouts are discussed in a section of their own.
Note
Setting names are case-insensitive, but the format used above is recommended. Prior to Robot Framework 3.1, settings were also space-insensitive meaning that extra
spaces could be added (e.g. [T a g s]). This is now deprecated and only the format above, case-insensitively, is supported. Possible space between brackets and the
name (e.g. [ Tags ]) is still allowed.
User keywords can have a documentation that is set with the [Documentation] setting. It supports same formatting, splitting to multiple lines, and other features
as test case documentation. This setting documents the user keyword in the test data. It is also shown in a more formal keyword documentation, which the
Libdoc tool can create from resource files. Finally, the first logical row of the documentation, until the first empty row, is shown as a keyword documentation in
test logs.
Multiline documentation
[Documentation] The first line creates the short doc.
...
... This is the body of the documentation.
... It is not shown in Libdoc outputs but only
... the short doc is shown in logs.
No Operation
Sometimes keywords need to be removed, replaced with new ones, or deprecated for other reasons. User keywords can be marked deprecated by starting the
documentation with *DEPRECATED*, which will cause a warning when the keyword is used. For more information, see the Deprecating keywords section.
Note
Prior to Robot Framework 3.1, the short documentation contained only the first physical line of the keyword documentation.
Keyword tags are shown in logs and in documentation generated by Libdoc, where the keywords can also be searched based on tags. The --removekeywords
and --flattenkeywords commandline options also support selecting keywords by tag, and new usages for keywords tags are possibly added in later releases.
Similarly as with test case tags, user keyword tags with robot- and robot: prefixes are reserved for special features by Robot Framework itself. Users should
thus not use any tag with these prefixes unless actually activating the special functionality.
The simplest way to specify arguments (apart from not having them at all) is using only positional arguments. In most cases, this is all that is needed.
The syntax is such that first the [Arguments] setting is given and then argument names are defined in the subsequent cells. Each argument is in its own cell,
using the same syntax as with variables. The keyword must be used with as many arguments as there are argument names in its signature. The actual argument
names do not matter to the framework, but from users' perspective they should be as descriptive as possible. It is recommended to use lower-case letters in
variable names, either as ${my_arg}, ${my arg} or ${myArg}.
Three Arguments
[Arguments] ${arg1} ${arg2} ${arg3}
Log 1st argument: ${arg1}
Log 2nd argument: ${arg2}
Log 3rd argument: ${arg3}
When creating user keywords, positional arguments are sufficient in most situations. It is, however, sometimes useful that keywords have default values for
some or all of their arguments. Also user keywords support default values, and the needed new syntax does not add very much to the already discussed basic
syntax.
In short, default values are added to arguments, so that first there is the equals sign (=) and then the value, for example ${arg}=default. There can be many
arguments with defaults, but they all must be given after the normal positional arguments. The default value can contain a variable created on test, suite or
global scope, but local variables of the keyword executor cannot be used. Starting from Robot Framework 3.0, default value can also be defined based on earlier
arguments accepted by the keyword.
Note
The syntax for default values is space sensitive. Spaces before the = sign are not allowed, and possible spaces after it are considered part of the default value itself.
When a keyword accepts several arguments with default values and only some of them needs to be overridden, it is often handy to use the named arguments
syntax. When this syntax is used with user keywords, the arguments are specified without the ${} decoration. For example, the second keyword above could be
used like below and ${arg1} would still get its default value.
As all Pythonistas must have already noticed, the syntax for specifying default arguments is heavily inspired by Python syntax for function default values.
Sometimes even default values are not enough and there is a need for a keyword accepting variable number of arguments. User keywords support also this
feature. All that is needed is having list variable such as @{varargs} after possible positional arguments in the keyword signature. This syntax can be combined
with the previously described default values, and at the end the list variable gets all the leftover arguments that do not match other arguments. The list variable
can thus have any number of items, even zero.
Notice that if the last keyword above is used with more than one argument, the second argument ${opt} always gets the given value instead of the default
value. This happens even if the given value is empty. The last example also illustrates how a variable number of arguments accepted by a user keyword can be
used in a for loop. This combination of two rather advanced functions can sometimes be very useful.
The keywords in the examples above could be used, for example, like this:
Again, Pythonistas probably notice that the variable number of arguments syntax is very close to the one in Python.
User keywords can also accept free named arguments by having a dictionary variable like &{named} as the absolutely last argument. When the keyword is
called, this variable will get all named arguments that do not match any positional argument or named-only argument in the keyword signature.
Run Program
[Arguments] @{args} &{config}
Run Process program.py @{args} &{config}
The last example above shows how to create a wrapper keyword that accepts any positional or named argument and passes them forward. See free named
argument examples for a full example with same keyword.
Free named arguments support with user keywords works similarly as kwargs work in Python. In the signature and also when passing arguments forward, &
{kwargs} is pretty much the same as Python's **kwargs.
Starting from Robot Framework 3.1, user keywords support named-only arguments that are inspired by Python 3 keyword-only arguments. This syntax is
typically used by having normal arguments after variable number of arguments (@{varargs}). If the keywords does not use varargs, it is possible to use just
@{} to denote that the subsequent arguments are named-only:
Without Varargs
[Arguments] @{} ${first} ${second}
Log Many ${first} ${second}
Named-only arguments can be used together with positional arguments as well as with free named arguments. When using free named arguments, they must be
last:
When passing named-only arguments to keywords, their order does not matter other than they must follow possible positional arguments. The keywords above
could be used, for example, like this:
Named-only arguments can have default values similarly as normal user keyword arguments. A minor difference is that the order of arguments with and
without default values is not important.
Basic syntax
It has always been possible to use keywords like Select dog from list and Selects cat from list, but all such keywords must have been implemented separately.
The idea of embedding arguments into the keyword name is that all you need is a keyword with name like Select ${animal} from list.
Keywords using embedded arguments cannot take any "normal" arguments (specified with [Arguments] setting) but otherwise they are created just like other
user keywords. The arguments used in the name will naturally be available inside the keyword and they have different value depending on how the keyword is
called. For example, ${animal} in the previous has value dog if the keyword is used like Select dog from list. Obviously it is not mandatory to use all these
arguments inside the keyword, and they can thus be used as wildcards.
These kind of keywords are also used the same way as other keywords except that spaces and underscores are not ignored in their names. They are, however,
case-insensitive like other keywords. For example, the keyword in the example above could be used like select x from list, but not like Select x fromlist.
Embedded arguments do not support default values or variable number of arguments like normal arguments do. Using variables when calling these keywords is
possible but that can reduce readability. Notice also that embedded arguments only work with user keywords.
One tricky part in using embedded arguments is making sure that the values used when calling the keyword match the correct arguments. This is a problem
especially if there are multiple arguments and characters separating them may also appear in the given values. For example, keyword Select ${city} ${team}
does not work correctly if used with city containing too parts like Select Los Angeles Lakers.
An easy solution to this problem is quoting the arguments (e.g. Select "${city}" "${team}") and using the keyword in quoted format (e.g. Select "Los Angeles"
"Lakers"). This approach is not enough to resolve all this kind of conflicts, though, but it is still highly recommended because it makes arguments stand out
from rest of the keyword. A more powerful but also more complicated solution, using custom regular expressions when defining variables, is explained in the
next section. Finally, if things get complicated, it might be a better idea to use normal positional arguments instead.
The problem of arguments matching too much occurs often when creating keywords that ignore given/when/then/and/but prefixes . For example, ${name} goes
home matches Given Janne goes home so that ${name} gets value Given Janne. Quotes around the argument, like in "${name}" goes home, resolve this
problem easily.
A solution to this problem is using a custom regular expression that makes sure that the keyword matches only what it should in that particular context. To be
able to use this feature, and to fully understand the examples in this section, you need to understand at least the basics of the regular expression syntax.
A custom embedded argument regular expression is defined after the base name of the argument so that the argument and the regexp are separated with a colon.
For example, an argument that should match only numbers can be defined like ${arg:\d+}. Using custom regular expressions is illustrated by the examples
below.
Today is ${date:\d{4\}-\d{2\}-\d{2\}}
Log ${date}
In the above example keyword I execute "ls" with "-lh" matches only I execute "${cmd}" with "${opts}". That is guaranteed because the custom regular
expression [^"]+ in I execute "${cmd:[^"]}" means that a matching argument cannot contain any quotes. In this case there is no need to add custom regexps to
the other I execute variant.
Tip
If you quote arguments, using regular expression [^"]+ guarantees that the argument matches only until the first closing quote.
Being implemented with Python, Robot Framework naturally uses Python's re module that has pretty standard regular expressions syntax. This syntax is
otherwise fully supported with embedded arguments, but regexp extensions in format (?...) cannot be used. Notice also that matching embedded arguments is
done case-insensitively. If the regular expression syntax is invalid, creating the keyword fails with an error visible in test execution errors.
There are some special characters that need to be escaped when used in the custom embedded arguments regexp. First of all, possible closing curly braces (}) in
the pattern need to be escaped with a single backslash (\}) because otherwise the argument would end already there. This is illustrated in the previous example
with keyword Today is ${date:\d{4\}-\d{2\}-\d{2\}}.
Backslash (\) is a special character in Python regular expression syntax and thus needs to be escaped if you want to have a literal backslash character. The
safest escape sequence in this case is four backslashes (\\\\) but, depending on the next character, also two backslashes may be enough.
Notice also that keyword names and possible embedded arguments in them should not be escaped using the normal test data escaping rules. This means that, for
example, backslashes in expressions like ${name:\w+} should not be escaped.
Whenever custom embedded argument regular expressions are used, Robot Framework automatically enhances the specified regexps so that they match
variables in addition to the text matching the pattern. This means that it is always possible to use variables with keywords having embedded arguments. For
example, the following test case would pass using the keywords from the earlier example.
*** Variables ***
${DATE} 2011-06-27
A drawback of variables automatically matching custom regular expressions is that it is possible that the value the keyword gets does not actually match the
specified regexp. For example, variable ${DATE} in the above example could contain any value and Today is ${DATE} would still match the same keyword.
The biggest benefit of having arguments as part of the keyword name is that it makes it easier to use higher-level sentence-like keywords when writing test
cases in behavior-driven style. The example below illustrates this. Notice also that prefixes Given, When and Then are left out of the keyword definitions.
Note
Embedded arguments feature in Robot Framework is inspired by how step definitions are created in a popular BDD tool Cucumber.
The most common case is that a user keyword returns one value and it is assigned to a scalar variable. When using the [Return] setting, this is done by having
the return value in the next cell after the setting.
User keywords can also return several values, which can then be assigned into several scalar variables at once, to a list variable, or to scalar variables and a list
variable. Several values can be returned simply by specifying those values in different cells after the [Return] setting.
Multiple Values
${a} ${b} ${c} = Return Three Values
@{list} = Return Three Values
${scalar} @{rest} = Return Three Values
BuiltIn keywords Return From Keyword and Return From Keyword If allow returning from a user keyword conditionally in the middle of the keyword. Both of
them also accept optional return values that are handled exactly like with the [Return] setting discussed above.
The first example below is functionally identical to the previous [Return] setting example. The second, and more advanced, example demonstrates returning
conditionally inside a for loop.
Advanced
@{list} = Create List foo baz
${index} = Find Index baz @{list}
Should Be Equal ${index} ${1}
${index} = Find Index non existing @{list}
Should Be Equal ${index} ${-1}
Find Index
[Arguments] ${element} @{items}
${index} = Set Variable ${0}
FOR ${item} IN @{items}
Return From Keyword If '${item}' == '${element}' ${index}
${index} = Set Variable ${index + 1}
END
Return From Keyword ${-1} # Could also use [Return]
Keyword teardown works much in the same way as a test case teardown. Most importantly, the teardown is always a single keyword, although it can be another
user keyword, and it gets executed also when the user keyword fails. In addition, all steps of the teardown are executed even if one of them fails. However, a
failure in keyword teardown will fail the test case and subsequent steps in the test are not run. The name of the keyword to be executed as a teardown can also
be a variable.
Using variables
[Documentation] Teardown given as variable
Do Something
[Teardown] ${TEARDOWN}
Variable files provide a powerful mechanism for creating and sharing variables. For example, they allow values other than strings and enable creating variables
dynamically. Their flexibility comes from the fact that they are created using Python code, which also makes them somewhat more complicated than Variable
tables.
Resource files are imported using the Resource setting in the Settings table. The path to the resource file is given in the cell after the setting name.
If the path is given in an absolute format, it is used directly. In other cases, the resource file is first searched relatively to the directory where the importing file is
located. If the file is not found there, it is then searched from the directories in Python's module search path. The path can contain variables, and it is
recommended to use them to make paths system-independent (for example, ${RESOURCES}/login_resources.robot or ${RESOURCE_PATH}). Additionally,
forward slashes (/) in the path are automatically changed to backslashes (\) on Windows.
Resource files can use all the same extensions as test case files created using the supported file formats. When using the plain text format, it is possible to use a
special .resource extension in addition to the normal .robot extensions. This makes it easier to separate test case files and resource files from each others.
The user keywords and variables defined in a resource file are available in the file that takes that resource file into use. Similarly available are also all keywords
and variables from the libraries, resource files and variable files imported by the said resource file.
Note
The higher-level structure of resource files is the same as that of test case files otherwise, but, of course, they cannot contain Test Case tables. Additionally, the
Setting table in resource files can contain only import settings (Library, Resource, Variables) and Documentation. The Variable table and Keyword table are
used exactly the same way as in test case files.
If several resource files have a user keyword with the same name, they must be used so that the keyword name is prefixed with the resource file name without
the extension (for example, myresources.Some Keyword and common.Some Keyword). Moreover, if several resource files contain the same variable, the one that
is imported first is taken into use.
Keywords created in a resource file can be documented using [Documentation] setting. The resource file itself can have Documentation in the Setting table
similarly as test suites.
Both Libdoc and RIDE use these documentations, and they are naturally available for anyone opening resource files. The first logical line of the documentation
of a keyword, until the first empty line, is logged when the keyword is run, but otherwise resource file documentation is ignored during the test execution.
Input Name
[Arguments] ${name}
Input Text username_field ${name}
Input Password
[Arguments] ${password}
Input Text password_field ${password}
Variable files are typically implemented as Python modules and there are two different approaches for creating variables:
Alternatively variable files can be implemented as Python or Java classes that the framework will instantiate. Also in this case it is possible to create variables
as attributes or get them from a special method.
Setting table
All test data files can import variables using the Variables setting in the Setting table, in the same way as resource files are imported using the Resource setting.
Similarly to resource files, the path to the imported variable file is considered relative to the directory where the importing file is, and if not found, it is searched
from the directories in the module search path. The path can also contain variables, and slashes are converted to backslashes on Windows. If an argument file
takes arguments, they are specified in the cells after the path and also they can contain variables.
All variables from a variable file are available in the test data file that imports it. If several variable files are imported and they contain a variable with the same
name, the one in the earliest imported file is taken into use. Additionally, variables created in Variable tables and set from the command line override variables
from variable files.
Command line
Another way to take variable files into use is using the command line option --variablefile. Variable files are referenced using a path to them, and possible
arguments are joined to the path with a colon (:):
--variablefile myvariables.py
--variablefile path/variables.py
--variablefile /absolute/path/common.py
--variablefile taking_arguments.py:arg1:arg2
Variable files taken into use from the command line are also searched from the module search path similarly as variable files imported in the Setting table.
If a variable file is given as an absolute Windows path, the colon after the drive letter is not considered a separator:
--variablefile C:\path\variables.py
It is also possible to use a semicolon (;) as an argument separator. This is useful if variable file arguments themselves contain colons, but requires surrounding
the whole value with quotes on UNIX-like operating systems:
--variablefile "myvariables.py;argument:with:colons"
--variablefile C:\path\variables.py;D:\data.xls
Variables in these variable files are globally available in all test data files, similarly as individual variables set with the --variable option. If both
--variablefile and --variable options are used and there are variables with same names, those that are set individually with --variable option take
precedence.
Basic syntax
When variable files are taken into use, they are imported as Python modules and all their global attributes that do not start with an underscore (_) are considered
to be variables. Because variable names are case-insensitive, both lower- and upper-case names are possible, but in general, capital letters are recommended for
global variables and attributes.
In the example above, variables ${VARIABLE}, ${ANOTHER VARIABLE}, and so on, are created. The first two variables are strings, the third one is an integer, then
there are two lists, and the final value is a dictionary. All these variables can be used as a scalar variable, lists and the dictionary also a list variable like
@{STRINGS} (in the dictionary's case that variable would only contain keys), and the dictionary also as a dictionary variable like &{MAPPING}.
To make creating a list variable or a dictionary variable more explicit, it is possible to prefix the variable name with LIST__ or DICT__, respectively:
These prefixes will not be part of the final variable name, but they cause Robot Framework to validate that the value actually is list-like or dictionary-like. With
dictionaries the actual stored value is also turned into a special dictionary that is used also when creating dictionary variables in the Variable table. Values of
these dictionaries are accessible as attributes like ${FINNISH.cat}. These dictionaries are also ordered, but preserving the source order requires also the
original dictionary to be ordered.
The variables in both the examples above could be created also using the Variable table below.
Note
Variables are not replaced in strings got from variable files. For example, VAR = "an ${example}" would create variable ${VAR} with a literal string value an
${example} regardless would variable ${example} exist or not.
Variables in variable files are not limited to having only strings or other base types as values like variable tables. Instead, their variables can contain any objects.
In the example below, the variable ${MAPPING} contains a Java Hashtable with two values (this example works only when running tests on Jython).
MAPPING = Hashtable()
MAPPING.put("one", 1)
MAPPING.put("two", 2)
The second example creates ${MAPPING} as a Python dictionary and also has two variables created from a custom object implemented in the same file.
class MyObject:
def __init__(self, name):
self.name = name
OBJ1 = MyObject('John')
OBJ2 = MyObject('Jane')
Because variable files are created using a real programming language, they can have dynamic logic for setting variables.
import os
import random
import time
The example above uses standard Python libraries to set different variables, but you can use your own code to construct the values. The example below
illustrates the concept, but similarly, your code could read the data from a database, from an external file or even ask it from the user.
import math
def get_area(diameter):
radius = diameter / 2
area = math.pi * radius * radius
return area
AREA1 = get_area(1)
AREA2 = get_area(2)
When Robot Framework processes variable files, all their attributes that do not start with an underscore are expected to be variables. This means that even
functions or classes created in the variable file or imported from elsewhere are considered variables. For example, the last example would contain the variables
${math} and ${get_area} in addition to ${AREA1} and ${AREA2}.
Normally the extra variables do not cause problems, but they could override some other variables and cause hard-to-debug errors. One possibility to ignore
other attributes is prefixing them with an underscore:
def _get_area(diameter):
radius = diameter / 2.0
area = _math.pi * radius * radius
return area
AREA1 = _get_area(1)
AREA2 = _get_area(2)
If there is a large number of other attributes, instead of prefixing them all, it is often easier to use a special attribute __all__ and give it a list of attribute names
to be processed as variables.
import math
def get_area(diameter):
radius = diameter / 2.0
area = math.pi * radius * radius
return area
AREA1 = get_area(1)
AREA2 = get_area(2)
Note
The __all__ attribute is also, and originally, used by Python to decide which attributes to import when using the syntax from modulename import *.
An alternative approach for getting variables is having a special get_variables function (also camelCase syntax getVariables is possible) in a variable file. If
such a function exists, Robot Framework calls it and expects to receive variables as a Python dictionary or a Java Map with variable names as keys and variable
values as values. Created variables can be used as scalars, lists, and dictionaries exactly like when creating variables directly, and it is possible to use LIST__
and DICT__ prefixes to make creating list and dictionary variables more explicit. The example below is functionally identical to the first creating variables
directly example.
def get_variables():
variables = {"VARIABLE ": "An example string",
"ANOTHER VARIABLE": "This is pretty easy!",
"INTEGER": 42,
"STRINGS": ["one", "two", "kolme", "four"],
"NUMBERS": [1, 42, 3.14],
"MAPPING": {"one": 1, "two": 2, "three": 3}}
return variables
get_variables can also take arguments, which facilitates changing what variables actually are created. Arguments to the function are set just as any other
arguments for a Python function. When taking variable files into use in the test data, arguments are specified in cells after the path to the variable file, and in the
command line they are separated from the path with a colon or a semicolon.
The dummy example below shows how to use arguments with variable files. In a more realistic example, the argument could be a path to an external text file or
database where to read variables from.
def get_variables(arg):
if arg == 'one':
return variables1
else:
return variables2
Implementation
Because variable files are always imported using a file system path, creating them as classes has some restrictions:
Python classes must have the same name as the module they are located.
Java classes must live in the default package.
Paths to Java classes must end with either .java or .class. The class file must exists in both cases.
Regardless the implementation language, the framework will create an instance of the class using no arguments and variables will be gotten from the instance.
Similarly as with modules, variables can be defined as attributes directly in the instance or gotten from a special get_variables (or getVariables) method.
When variables are defined directly in an instance, all attributes containing callable values are ignored to avoid creating variables from possible methods the
instance has. If you would actually need callable variables, you need to use other approaches to create variable files.
Examples
The first examples create variables from attributes using both Python and Java. Both of them create variables ${VARIABLE} and @{LIST} from class attributes
and ${ANOTHER VARIABLE} from an instance attribute.
class StaticPythonExample(object):
variable = 'value'
LIST__list = [1, 2, 3]
_not_variable = 'starts with an underscore'
def __init__(self):
self.another_variable = 'another value'
public StaticJavaExample() {
anotherVariable = "another value";
}
}
The second examples utilizes dynamic approach for getting variables. Both of them create only one variable ${DYNAMIC VARIABLE}.
class DynamicPythonExample(object):
import java.util.Map;
import java.util.HashMap;
Variable files can also be implemented as YAML files. YAML is a data serialization language with a simple and human-friendly syntax. The following example
demonstrates a simple YAML file:
Using YAML files with Robot Framework requires PyYAML module to be installed. If you have pip installed, you can install it simply by running pip install
pyyaml.
YAML support is new in Robot Framework 2.9. Starting from version 2.9.2, the standalone JAR distribution has PyYAML included by default.
YAML variable files can be used exactly like normal variable files from the command line using --variablefile option, in the settings table using Variables
setting, and dynamically using the Import Variables keyword. The only thing to remember is that paths to YAML files must always end with .yaml extension.
If the above YAML file is imported, it will create exactly the same variables as the following variable table:
YAML files used as variable files must always be mappings in the top level. As the above example demonstrates, keys and values in the mapping become
variable names and values, respectively. Variable values can be any data types supported by YAML syntax. If names or values contain non-ASCII characters,
YAML variables files must be UTF-8 encoded.
Mappings used as values are automatically converted to special dictionaries that are used also when creating dictionary variables in the variable table. Most
importantly, values of these dictionaries are accessible as attributes like ${DICT.one}, assuming their names are valid as Python attribute names. If the name
contains spaces or is otherwise not a valid attribute name, it is always possible to access dictionary values using syntax like &{DICT}[with spaces] syntax.
The created dictionaries are also ordered, but unfortunately the original source order of in the YAML file is not preserved.
Keyword scopes
When only a keyword name is used and there are several keywords with that name, Robot Framework attempts to determine which keyword has the highest
priority based on its scope. The keyword's scope is determined on the basis of how the keyword in question is created:
1. Created as a user keyword in the same file where it is used. These keywords have the highest priority and they are always used, even if there are other
keywords with the same name elsewhere.
2. Created in a resource file and imported either directly or indirectly from another resource file. This is the second-highest priority.
3. Created in an external test library. These keywords are used, if there are no user keywords with the same name. However, if there is a keyword with the
same name in the standard library, a warning is displayed.
4. Created in a standard library. These keywords have the lowest priority.
Specifying a keyword explicitly
Scopes alone are not a sufficient solution, because there can be keywords with the same name in several libraries or resources, and thus, they provide a
mechanism to use only the keyword of the highest priority. In such cases, it is possible to use the full name of the keyword, where the keyword name is prefixed
with the name of the resource or library and a dot is a delimiter.
With library keywords, the long format means only using the format LibraryName.Keyword Name. For example, the keyword Run from the OperatingSystem
library could be used as OperatingSystem.Run, even if there was another Run keyword somewhere else. If the library is in a module or package, the full module
or package name must be used (for example, com.company.Library.Some Keyword). If a custom name is given to a library using the WITH NAME syntax, the
specified name must be used also in the full keyword name.
Resource files are specified in the full keyword name, similarly as library names. The name of the resource is derived from the basename of the resource file
without the file extension. For example, the keyword Example in a resource file myresources.html can be used as myresources.Example. Note that this syntax
does not work, if several resource files have the same basename. In such cases, either the files or the keywords must be renamed. The full name of the keyword
is case-, space- and underscore-insensitive, similarly as normal keyword names.
If there are multiple conflicts between keywords, specifying all the keywords in the long format can be quite a lot work. Using the long format also makes it
impossible to create dynamic test cases or user keywords that work differently depending on which libraries or resources are available. A solution to both of
these problems is specifying the keyword priorities explicitly using the keyword Set Library Search Order from the BuiltIn library.
Note
Although the keyword has the word library in its name, it works also with resource files. As discussed above, keywords in resources always have higher
priority than keywords in libraries, though.
The Set Library Search Order accepts an ordered list or libraries and resources as arguments. When a keyword name in the test data matches multiple
keywords, the first library or resource containing the keyword is selected and that keyword implementation used. If the keyword is not found from any of the
specified libraries or resources, execution fails for conflict the same way as when the search order is not set.
For more information and examples, see the documentation of the keyword.
2.9.2 Timeouts
Keywords may be problematic in situations where they take exceptionally long to execute or just hang endlessly. Robot Framework allows you to set timeouts
both for test cases and user keywords, and if a test or keyword is not finished within the specified time, the keyword that is currently being executed is
forcefully stopped. Stopping keywords in this manner may leave the library or system under test to an unstable state, and timeouts are recommended only when
there is no safer option available. In general, libraries should be implemented so that keywords cannot hang or that they have their own timeout mechanism, if
necessary.
The test case timeout can be set either by using the Test Timeout setting in the Setting table or the [Timeout] setting in the Test Case table. Test Timeout in the
Setting table defines a default test timeout value for all the test cases in the test suite, whereas [Timeout] in the Test Case table applies a timeout to an individual
test case and overrides the possible default value.
Using an empty [Timeout] means that the test has no timeout even when Test Timeout is used. It is also possible to use value NONE for this purpose.
Regardless of where the test timeout is defined, the first cell after the setting name contains the duration of the timeout. The duration must be given in Robot
Framework's time format, that is, either directly in seconds or in a format like 1 minute 30 seconds. It must be noted that there is always some overhead by
the framework, and timeouts shorter than one second are thus not recommended.
The default error message displayed when a test timeout occurs is Test timeout <time> exceeded. It is also possible to use custom error messages, and these
messages are written into the cells after the timeout duration. The message can be split into multiple cells, similarly as documentations. Both the timeout value
and the error message may contain variables.
If there is a timeout, the keyword running is stopped at the expiration of the timeout and the test case fails. However, keywords executed as test teardown are
not interrupted if a test timeout occurs, because they are normally engaged in important clean-up activities. If necessary, it is possible to interrupt also these
keywords with user keyword timeouts.
Override
[Documentation] Override default, use 10 seconds timeout
[Timeout] 10
Some Keyword argument
Custom Message
[Documentation] Override default and use custom message
[Timeout] 1min 10s This is my custom error
Some Keyword argument
Variables
[Documentation] It is possible to use variables too
[Timeout] ${TIMEOUT}
Some Keyword argument
No Timeout
[Documentation] Empty timeout means no timeout even when Test Timeout has been used
[Timeout]
Some Keyword argument
No Timeout 2
[Documentation] Disabling timeout with NONE works too and is more explicit.
[Timeout] NONE
Some Keyword argument
A timeout can be set for a user keyword using the [Timeout] setting in the Keyword table. The syntax for setting it, including how timeout values and possible
custom messages are given, is identical to the syntax used with test case timeouts. If no custom message is provided, the default error message Keyword
timeout <time> exceeded is used if a timeout occurs.
Starting from Robot Framework 3.0, timeout can be specified as a variable so that the variable value is given as an argument. Using global variables works
already with previous versions.
A user keyword timeout is applicable during the execution of that user keyword. If the total time of the whole keyword is longer than the timeout value, the
currently executed keyword is stopped. User keyword timeouts are applicable also during a test case teardown, whereas test timeouts are not.
If both the test case and some of its keywords (or several nested keywords) have a timeout, the active timeout is the one with the least time left.
For loops can be used with both test cases and user keywords. Except for really simple cases, user keywords are better, because they hide the complexity
introduced by for loops. The basic for loop syntax, FOR item IN sequence, is derived from Python, but similar syntax is supported also by various other
programming languages.
In a normal for loop, one variable is assigned based on a list of values, one value per iteration. The syntax starts with FOR (case-sensitive) as a marker, then the
loop variable, then a mandatory IN (case-sensitive) as a separator, and finally the values to iterate. These values can contain variables, including list variables.
The keywords used in the for loop are on the following rows and the loop ends with END (case-sensitive) on its own row. Keywords inside the loop do not need
to be indented, but that is highly recommended to make the syntax easier to read.
Second Example
FOR ${var} IN one two ${3} four ${five}
... kuusi 7 eight nine ${last}
Log ${var}
END
The for loop in Example above is executed twice, so that first the loop variable ${animal} has the value cat and then dog. The loop consists of two Log
keywords. In the second example, loop values are split into two rows and the loop is run altogether ten times.
It is often convenient to use for loops with list variables. This is illustrated by the example below, where @{ELEMENTS} contains an arbitrarily long list of
elements and keyword Start Element is used with all of them one by one.
For loop syntax was enhanced in various ways in Robot Framework 3.1. The most noticeable change was that loops nowadays end with the explicit END marker
and keywords inside the loop do not need to be indented. In the space separated plain text format indentation required escaping with a backslash which resulted
in quite ugly syntax:
Another change, also visible in the example above, was that the for loop marker used to be :FOR when nowadays just FOR is enough. Related to that, the :FOR
marker and also the IN separator were case-insensitive but nowadays both FOR and IN are case-sensitive.
Old for loop syntax still works in Robot Framework 3.1 and only using IN case-insensitively causes a deprecation warning. Not closing loops with END,
escaping keywords inside loops with \, and using :FOR instead of FOR are all going to be deprecated in Robot Framework 3.2. Users are advised to switch to the
new syntax as soon as possible.
When using the pipe separated format, escaping with \ has not been needed:
The above syntax still works with Robot Framework 3.1, but it will not work anymore in Robot Framework 3.2. The recommended solution is closing the loop
with an explicit END, but if old Robot Framework versions need to be supported then escaping with \ is needed.
Having nested for loops is not supported directly, but it is possible to use a user keyword inside a for loop and have another for loop there.
Handle Row
[Arguments] @{row}
FOR ${cell} IN @{row}
Handle Cell ${cell}
END
It is also possible to use several loop variables. The syntax is the same as with the normal for loop, but all loop variables are listed in the cells between FOR and
IN. There can be any number of loop variables, but the number of values must be evenly dividable by the number of variables.
If there are lot of values to iterate, it is often convenient to organize them below the loop variables, as in the first loop of the example below:
For-in-range loop
Earlier for loops always iterated over a sequence, and this is also the most common use case. Sometimes it is still convenient to have a for loop that is executed
a certain number of times, and Robot Framework has a special FOR index IN RANGE limit syntax for this purpose. This syntax is derived from the similar
Python idiom using the built-in range() function.
Similarly as other for loops, the for-in-range loop starts with FOR and the loop variable is in the next cell. In this format there can be only one loop variable and
it contains the current loop index. The next cell must contain IN RANGE (case-sensitive) and the subsequent cells loop limits.
In the simplest case, only the upper limit of the loop is specified. In this case, loop indexes start from zero and increase by one until, but excluding, the limit. It
is also possible to give both the start and end limits. Then indexes start from the start limit, but increase similarly as in the simple case. Finally, it is possible to
give also the step value that specifies the increment to use. If the step is negative, it is used as decrement.
It is possible to use simple arithmetic such as addition and subtraction with the range limits. This is especially useful when the limits are specified with
variables. Start, end and step are typically given as integers, but using float values is possible as well.
Negative step
[Documentation] Loops over values 13, 3, and -7
FOR ${index} IN RANGE 13 -13 -10
Log ${index}
END
Arithmetic
[Documentation] Arithmetic with variable
FOR ${index} IN RANGE ${var} + 1
Log ${index}
END
Float parameters
[Documentation] Loops over values 3.14, 4.34, and 5.54
FOR ${index} IN RANGE 3.14 6.09 1.2
Log ${index}
END
Note
Prior to Robot Framework 3.1, the IN RANGE separator was both case- and space-insensitive. Such usage is nowadays deprecated and exactly IN RANGE is required.
For-in-enumerate loop
Sometimes it is useful to loop over a list and also keep track of your location inside the list. Robot Framework has a special FOR index ... IN ENUMERATE
... syntax for this situation. This syntax is derived from the Python built-in enumerate() function.
For-in-enumerate loops work just like regular for loops, except the cell after its loop variables must say IN ENUMERATE (case-sensitive), and they must have an
additional index variable before any other loop-variables. That index variable has a value of 0 for the first iteration, 1 for the second, etc.
For example, the following two test cases do the same thing:
For-in-enumerate
FOR ${index} ${item} IN ENUMERATE @{LIST}
My Keyword ${index} ${item}
END
Just like with regular for loops, you can loop over multiple values per loop iteration as long as the number of values in your list is evenly divisible by the
number of loop-variables (excluding the first, index variable).
Note
Prior to Robot Framework 3.1, the IN ENUMERATE separator was both case- and space-insensitive. Such usage is nowadays deprecated and exactly IN ENUMERATE is
required.
For-in-zip loop
Some tests build up several related lists, then loop over them together. Robot Framework has a shortcut for this case: FOR ... IN ZIP ..., which is derived
from the Python built-in zip() function.
For-in-zip
FOR ${number} ${name} IN ZIP ${NUMBERS} ${NAMES}
Number Should Be Named ${number} ${name}
END
Similarly as for-in-range and for-in-enumerate loops, for-in-zip loops require the cell after the loop variables to read IN ZIP (case-sensitive).
Values used with for-in-zip loops must be lists or list-like objects, and there must be same number of loop variables as lists to loop over. Looping will stop
when the shortest list is exhausted.
Note that any lists used with for-in-zip should usually be given as scalar variables like ${list}. A list variable only works if its items themselves are lists.
Note
Prior to Robot Framework 3.1, the IN ZIP separator was both case- and space-insensitive. Such usage is nowadays deprecated and exactly IN ZIP is required.
Exiting for loop
Normally for loops are executed until all the loop values have been iterated or a keyword used inside the loop fails. If there is a need to exit the loop earlier,
BuiltIn keywords Exit For Loop and Exit For Loop If can be used to accomplish that. They works similarly as break statement in Python, Java, and many other
programming languages.
Exit For Loop and Exit For Loop If keywords can be used directly inside a for loop or in a keyword that the loop uses. In both cases test execution continues
after the loop. It is an error to use these keywords outside a for loop.
In the above example it would be possible to use Exit For Loop If instead of using Exit For Loop with Run Keyword If. For more information about these
keywords, including more usage examples, see their documentation in the BuiltIn library.
In addition to exiting a for loop prematurely, it is also possible to continue to the next iteration of the loop before all keywords have been executed. This can be
done using BuiltIn keywords Continue For Loop and Continue For Loop If, that work like continue statement in many programming languages.
Continue For Loop and Continue For Loop If keywords can be used directly inside a for loop or in a keyword that the loop uses. In both cases rest of the
keywords in that iteration are skipped and execution continues from the next iteration. If these keywords are used on the last iteration, execution continues after
the loop. It is an error to use these keywords outside a for loop.
For more information about these keywords, including usage examples, see their documentation in the BuiltIn library.
For loops with multiple iterations often create lots of output and considerably increase the size of the generated output and log files. It is possible to remove
unnecessary keywords from the outputs using --RemoveKeywords FOR command line option.
For loops can be excessive in situations where there is only a need to repeat a single keyword. In these cases it is often easier to use BuiltIn keyword Repeat
Keyword. This keyword takes a keyword and how many times to repeat it as arguments. The times to repeat the keyword can have an optional postfix times or
x to make the syntax easier to read.
The name of the keyword used as a setup or a teardown of both test cases and test suites can be specified using a variable. This facilitates changing them,
for example, from the command line.
The BuiltIn keyword Run Keyword takes a keyword to actually execute as an argument, and it can thus be a variable. The value of the variable can, for
example, be got dynamically from an earlier keyword or given from the command line.
The BuiltIn keywords Run Keyword If and Run Keyword Unless execute a named keyword only if a certain expression is true or false, respectively. They
are ideally suited to creating simple if/else constructs. For an example, see the documentation of the former.
Another BuiltIn keyword, Set Variable If, can be used to set variables dynamically based on a given expression.
There are several BuiltIn keywords that allow executing a named keyword only if a test case or test suite has failed or passed.
2.9.5 Parallel execution of keywords
When parallel execution is needed, it must be implemented in test library level so that the library executes the code on background. Typically this means that
the library needs a keyword like Start Something that starts the execution and returns immediately, and another keyword like Get Results From Something that
waits until the result is available and returns it. See OperatingSystem library keywords Start Process and Read Process Output for an example.
Synopsis
Test execution is normally started using the robot runner script. Alternatively it is possible to execute the installed robot module or robot directory directly
using the selected interpreter. The final alternative is using the standalone JAR distribution.
Note
The robot script is new in Robot Framework 3.0. Prior to that, there were pybot, jybot and ipybot scripts that executed tests using Python, Jython and IronPython,
respectively. These scripts were removed in Robot Framework 3.1 and nowadays robot must be used regardless the interpreter.
Regardless of execution approach, the path (or paths) to the test data to be executed is given as an argument after the command. Additionally, different
command line options can be used to alter the test execution or generated outputs in many ways.
Specifying test data to be executed
Robot Framework test cases are created in files and directories, and they are executed by giving the path to the file or directory in question to the selected
runner script. The path can be absolute or, more commonly, relative to the directory where tests are executed from. The given file or directory creates the top-
level test suite, which gets its name, unless overridden with the --name option, from the file or directory name. Different execution possibilities are illustrated in
the examples below. Note that in these examples, as well as in other examples in this section, only the robot script is used, but other execution approaches
could be used similarly.
robot tests.robot
robot path/to/my_tests/
robot c:\robot\tests.robot
It is also possible to give paths to several test case files or directories at once, separated with spaces. In this case, Robot Framework creates the top-level test
suite automatically, and the specified files and directories become its child test suites. The name of the created test suite is got from child suite names by
catenating them together with an ampersand (&) and spaces. For example, the name of the top-level suite in the first example below is My Tests & Your Tests.
These automatically created names are often quite long and complicated. In most cases, it is thus better to use the --name option for overriding it, as in the
second example below:
Using options
When options are used, they must always be given between the runner script and the data sources. For example:
Options always have a long name, such as --name, and the most frequently needed options also have a short name, such as -N. In addition to that, long options
can be shortened as long as they are unique. For example, --logle DEBUG works, while --lo log.html does not, because the former matches only
--loglevel, but the latter matches several options. Short and shortened options are practical when executing test cases manually, but long options are
recommended in start-up scripts, because they are easier to understand.
The long option format is case-insensitive, which facilitates writing option names in an easy-to-read format. For example, --SuiteStatLevel is equivalent to,
but easier to read than --suitestatlevel.
Most of the options require a value, which is given after the option name. Both short and long options accept the value separated from the option name with a
space, as in --include tag or -i tag. With long options, the separator can also be the equals sign, for example --include=tag, and with short options the
separator can be omitted, as in -itag.
Some options can be specified several times. For example, --variable VAR1:value --variable VAR2:another sets two variables. If the options that take
only one value are used several times, the value given last is effective.
Options accepting no values can be disabled by using the same option again with no prefix added or dropped. The last option has precedence regardless of how
many times options are used. For example, --dryrun --dryrun --nodryrun --nostatusrc --statusrc would not activate the dry-run mode and would
return normal status rc.
Note
Support for adding or dropping no prefix is a new feature in Robot Framework 2.9. In earlier versions options accepting no values could be disabled by using the exact
same option again.
Simple patterns
Many command line options take arguments as simple patterns. These glob-like patterns are matched according to the following rules:
Examples:
All matching in above examples is case, space and underscore insensitive. For example, the second example would also match test named example 1.
Note
Support for brackets like [abc] and [!a-z] is new in Robot Framework 3.1.
Tag patterns
Most tag related options accept arguments as tag patterns. They have all the same characteristics as simple patterns, but they also support AND, OR and NOT
operators explained below. These operators can be used for combining two or more individual tags or patterns together.
AND or &
The whole pattern matches if all individual patterns match. AND and & are equivalent:
--include fooORbar # Matches tests containing either tag 'foo' or tag 'bar'.
--exclude xxORyyORzz # Matches tests containing any of tags 'xx', 'yy', or 'zz'.
NOT
The whole pattern matches if the pattern on the left side matches but the one on the right side does not. If used multiple times, none of the patterns after
the first NOT must not match:
--include fooNOTbar # Matches tests containing tag 'foo' but not tag 'bar'.
--exclude xxNOTyyNOTzz # Matches tests containing tag 'xx' but not tag 'yy' or tag 'zz'.
Starting from Robot Framework 2.9 the pattern can also start with NOT in which case the pattern matches if the pattern after NOT does not match:
The above operators can also be used together. The operator precedence, from highest to lowest, is AND, OR and NOT:
--include xANDyORz # Matches tests containing either tags 'x' and 'y', or tag 'z'.
--include xORyNOTz # Matches tests containing either tag 'x' or 'y', but not tag 'z'.
--include xNOTyANDz # Matches tests containing tag 'x', but not tags 'y' and 'z'.
Although tag matching itself is case-insensitive, all operators are case-sensitive and must be written with upper case letters. If tags themselves happen to
contain upper case AND, OR or NOT, they need to specified using lower case letters to avoid accidental operator usage:
Environment variables ROBOT_OPTIONS and REBOT_OPTIONS can be used to specify default options for test execution and result post-processing, respectively.
The options and their values must be defined as a space separated list and they are placed in front of any explicit options on the command line. The main use
case for these environment variables is setting global default values for certain options to avoid the need to repeat them every time tests are run or Rebot used.
Note
Possibility to have spaces in values by surrounding them in quotes is new in Robot Framework 2.9.2.
The most visible output from test execution is the output displayed in the command line. All executed test suites and test cases, as well as their statuses, are
shown there in real time. The example below shows the output from executing a simple test suite with only two test cases:
==============================================================================
Example test suite
==============================================================================
First test :: Possible test documentation | PASS |
------------------------------------------------------------------------------
Second test | FAIL |
Error message is displayed here
==============================================================================
Example test suite | FAIL |
2 critical tests, 1 passed, 1 failed
2 tests total, 1 passed, 1 failed
==============================================================================
Output: /path/to/output.xml
Report: /path/to/report.html
Log: /path/to/log.html
There is also a notification on the console whenever a top-level keyword in a test case ends. A green dot is used if a keyword passes and a red F if it fails. These
markers are written to the end of line and they are overwritten by the test status when the test itself ends. Writing the markers is disabled if console output is
redirected to a file.
The command line output is very limited, and separate output files are normally needed for investigating the test results. As the example above shows, three
output files are generated by default. The first one is in XML format and contains all the information about test execution. The second is a higher-level report
and the third is a more detailed log file. These files and other possible output files are discussed in more detail in the section Different output files.
Return codes
Runner scripts communicate the overall test execution status to the system running them using return codes. When the execution starts successfully and no
critical test fail, the return code is zero. All possible return codes are explained in the table below.
Return codes should always be easily available after the execution, which makes it easy to automatically determine the overall execution status. For example, in
bash shell the return code is in special variable $?, and in Windows it is in %ERRORLEVEL% variable. If you use some external tool for running tests, consult its
documentation for how to get the return code.
The return code can be set to 0 even if there are critical failures using the --NoStatusRC command line option. This might be useful, for example, in continuous
integration servers where post-processing of results is needed before the overall status of test execution can be determined.
Note
During the test execution there can be unexpected problems like failing to import a library or a resource file or a keyword being deprecated. Depending on the
severity such problems are categorized as errors or warnings and they are written into the console (using the standard error stream), shown on a separate Test
Execution Errors section in log files, and also written into Robot Framework's own system log. Normally these errors and warnings are generated by Robot
Framework itself, but libraries can also log errors and warnings. Example below illustrates how errors and warnings look like in the log file.
20090322 19:58:42.528 ERROR Error in file '/home/robot/tests.robot' in table
'Setting' in element on row 2: Resource file
'resource.robot' does not exist
20090322 19:58:43.931 WARN Keyword 'SomeLibrary.Example Keyword' is
deprecated. Use keyword `Other Keyword` instead.
Argument files are taken into use with --argumentfile (-A) option along with possible other command line options.
Note
Unlike other long command line options, --argumentfile cannot be given in shortened format like --argumentf. Additionally, using it case-insensitively like
--ArgumentFile is only supported by Robot Framework 3.0.2 and newer.
Argument files can contain both command line options and paths to the test data, one option or data source per line. Both short and long options are supported,
but the latter are recommended because they are easier to understand. Argument files can contain any characters without escaping, but spaces in the beginning
and end of lines are ignored. Additionally, empty lines and lines starting with a hash mark (#) are ignored:
In the above example the separator between options and their values is a single space. It is possible to use either an equal sign (=) or any number of spaces. As
an example, the following three lines are identical:
--name An Example
--name=An Example
--name An Example
If argument files contain non-ASCII characters, they must be saved using UTF-8 encoding.
Argument files can be used either alone so that they contain all the options and paths to the test data, or along with other options and paths. When an argument
file is used with other arguments, its contents are placed into the original list of arguments to the same place where the argument file option was. This means
that options in argument files can override options before it, and its options can be overridden by options after it. It is possible to use --argumentfile option
multiple times or even recursively:
Special argument file name STDIN can be used to read arguments from the standard input stream instead of a file. This can be useful when generating arguments
with a script:
All runner scripts also support getting the version information with the option --version. This information also contains Python or Jython version and the
platform type:
$ robot --version
Robot Framework 3.1 (Jython 2.7.0 on java1.7.0_45)
C:\>rebot --version
Rebot 3.1 (Python 3.7.0 on win32)
In UNIX-like environments, shell scripts provide a simple but powerful mechanism for creating custom start-up scripts. Windows batch files can also be used,
but they are more limited and often also more complicated. A platform-independent alternative is using Python or some other high-level programming
language. Regardless of the language, it is recommended that long option names are used, because they are easier to understand than the short names.
In the first examples, the same web tests are executed with different browsers and the results combined afterwards. This is easy with shell scripts, as practically
you just list the needed commands one after another:
#!/bin/bash
robot --variable BROWSER:Firefox --name Firefox --log none --report none --output out/fx.xml login
robot --variable BROWSER:IE --name IE --log none --report none --output out/ie.xml login
rebot --name Login --outputdir out --output login.xml out/fx.xml out/ie.xml
Implementing the above example with Windows batch files is not very complicated, either. The most important thing to remember is that because robot and
rebot scripts are implemented as batch files on Windows, call must be used when running them from another batch file. Otherwise execution would end when
the first batch file is finished.
@echo off
call robot --variable BROWSER:Firefox --name Firefox --log none --report none --output out\fx.xml login
call robot --variable BROWSER:IE --name IE --log none --report none --output out\ie.xml login
call rebot --name Login --outputdir out --output login.xml out\fx.xml out\ie.xml
In the next examples, jar files under the lib directory are put into CLASSPATH before starting the test execution. In these examples, start-up scripts require that
paths to the executed test data are given as arguments. It is also possible to use command line options freely, even though some options have already been set in
the script. All this is relatively straight-forward using bash:
#!/bin/bash
cp=.
for jar in lib/*.jar; do
cp=$cp:$jar
done
export CLASSPATH=$cp
Implementing this using Windows batch files is slightly more complicated. The difficult part is setting the variable containing the needed JARs inside a For
loop, because, for some reason, that is not possible without a helper function.
@echo off
set CP=.
for %%jar in (lib\*.jar) do (
call :set_cp %%jar
)
set CLASSPATH=%CP%
goto :eof
Sometimes when using Jython there is need to alter the Java startup parameters. The most common use case is increasing the JVM maximum memory size as
the default value may not be enough for creating reports and logs when outputs are very big. There are two easy ways to configure JVM options:
1. Set JYTHON_OPTS environment variable. This can be done permanently in operating system level or per execution in a custom start-up script.
2. Pass the needed Java parameters with -J option to Jython that will pass them forward to Java. This is especially easy when executing installed robot
module directly:
jython -J-Xmx1024m -m robot tests.robot
When a failure is caused by the tested application, the error message and log messages ought to be enough to understand what caused it. If that is not the case,
the test library does not provide enough information and needs to be enhanced. In this situation running the same test manually, if possible, may also reveal
more information about the issue.
Failures caused by test cases themselves or by keywords they use can sometimes be hard to debug. If the error message, for example, tells that a keyword is
used with wrong number of arguments fixing the problem is obviously easy, but if a keyword is missing or fails in unexpected way finding the root cause can be
harder. The first place to look for more information is the execution errors section in the log file. For example, an error about a failed test library import may
well explain why a test has failed due to a missing keyword.
If the log file does not provide enough information by default, it is possible to execute tests with a lower log level. For example tracebacks showing where in
the code the failure occurred are logged using the DEBUG level, and this information is invaluable when the problem is in an individual library keyword.
Logged tracebacks do not contain information about methods inside Robot Framework itself. If you suspect an error is caused by a bug in the framework, you
can enable showing internal traces by setting environment variable ROBOT_INTERNAL_TRACES to any non-empty value. This functionality is new in Robot
Framework 2.9.2.
If the log file still does not have enough information, it is a good idea to enable the syslog and see what information it provides. It is also possible to add some
keywords to the test cases to see what is going on. Especially BuiltIn keywords Log and Log Variables are useful. If nothing else works, it is always possible to
search help from mailing lists or elsewhere.
It is also possible to use the pdb module from the Python standard library to set a break point and interactively debug a running test. The typical way of
invoking pdb by inserting:
at the location you want to break into debugger will not work correctly with Robot Framework, as the standard output stream is redirected during keyword
execution. Instead, you can use the following:
Test cases are always executed within a test suite. A test suite created from a test case file has tests directly, whereas suites created from directories have child
test suites which either have tests or their own child suites. By default all the tests in an executed suite are run, but it is possible to select tests using options
--test, --suite, --include and --exclude. Suites containing no tests are ignored.
The execution starts from the top-level test suite. If the suite has tests they are executed one-by-one, and if it has suites they are executed recursively in depth-
first order. When an individual test case is executed, the keywords it contains are run in a sequence. Normally the execution of the current test ends if any of the
keywords fails, but it is also possible to continue after failures. The exact execution order and how possible setups and teardowns affect the execution are
discussed in the following sections.
Setups and teardowns can be used on test suite, test case and user keyword levels.
Suite setup
If a test suite has a setup, it is executed before its tests and child suites. If the suite setup passes, test execution continues normally. If it fails, all the test cases
the suite and its child suites contain are marked failed. The tests and possible suite setups and teardowns in the child test suites are not executed.
Suite setups are often used for setting up the test environment. Because tests are not run if the suite setup fails, it is easy to use suite setups for verifying that the
environment is in state in which the tests can be executed.
Suite teardown
If a test suite has a teardown, it is executed after all its test cases and child suites. Suite teardowns are executed regardless of the test status and even if the
matching suite setup fails. If the suite teardown fails, all tests in the suite are marked failed afterwards in reports and logs.
Suite teardowns are mostly used for cleaning up the test environment after the execution. To ensure that all these tasks are done, all the keywords used in the
teardown are executed even if some of them fail.
Test setup
Possible test setup is executed before the keywords of the test case. If the setup fails, the keywords are not executed. The main use for test setups is setting up
the environment for that particular test case.
Test teardown
Possible test teardown is executed after the test case has been executed. It is executed regardless of the test status and also if test setup has failed.
Similarly as suite teardown, test teardowns are used mainly for cleanup activities. Also they are executed fully even if some of their keywords fail.
Keyword teardown
User keywords cannot have setups, but they can have teardowns that work exactly like other teardowns. Keyword teardowns are run after the keyword is
executed otherwise, regardless the status, and they are executed fully even if some of their keywords fail.
Execution order
Test cases in a test suite are executed in the same order as they are defined in the test case file. Test suites inside a higher level test suite are executed in case-
insensitive alphabetical order based on the file or directory name. If multiple files and/or directories are given from the command line, they are executed in the
order they are given.
If there is a need to use certain test suite execution order inside a directory, it is possible to add prefixes like 01 and 02 into file and directory names. Such
prefixes are not included in the generated test suite name if they are separated from the base name of the suite with two underscores:
If the alphabetical ordering of test suites inside suites is problematic, a good workaround is giving them separately in the required order. This easily leads to
overly long start-up commands, but argument files allow listing files nicely one file per line.
It is also possible to randomize the execution order using the --randomize option.
Passing execution
Typically test cases, setups and teardowns are considered passed if all keywords they contain are executed and none of them fail. It is also possible to use
BuiltIn keywords Pass Execution and Pass Execution If to stop execution with PASS status and skip the remaining keywords.
How Pass Execution and Pass Execution If behave in different situations is explained below:
When used in any setup or teardown (suite, test or keyword), these keywords pass that setup or teardown. Possible teardowns of the started keywords are
executed. Test execution or statuses are not affected otherwise.
When used in a test case outside setup or teardown, the keywords pass that particular test case. Possible test and keyword teardowns are executed.
Possible continuable failures that occur before these keyword are used, as well as failures in teardowns executed afterwards, will fail the execution.
It is mandatory to give an explanation message why execution was interrupted, and it is also possible to modify test case tags. For more details, and usage
examples, see the documentation of these keywords.
Passing execution in the middle of a test, setup or teardown should be used with care. In the worst case it leads to tests that skip all the parts that could actually
uncover problems in the tested application. In cases where execution cannot continue do to external factors, it is often safer to fail the test case and make it non-
critical.
Run Keyword And Ignore Error and Run Keyword And Expect Error keywords
BuiltIn keywords Run Keyword And Ignore Error and Run Keyword And Expect Error handle failures so that test execution is not terminated immediately.
Though, using these keywords for this purpose often adds extra complexity to test cases, so the following features are worth considering to make continuing
after failures easier.
Library keywords report failures using exceptions, and it is possible to use special exceptions to tell the core framework that execution can continue regardless
the failure. How these exceptions can be created is explained in the test library API chapter.
When a test ends and there has been one or more continuable failure, the test will be marked failed. If there are more than one failure, all of them will be
enumerated in the final error message:
Test execution ends also if a normal failure occurs after continuable failures. Also in that case all the failures will be listed in the final error message.
The return value from failed keywords, possibly assigned to a variable, is always the Python None.
BuiltIn keyword Run Keyword And Continue On Failure allows converting any failure into a continuable failure. These failures are handled by the framework
exactly the same way as continuable failures originating from library keywords.
To make it sure that all the cleanup activities are taken care of, the continue on failure mode is automatically on in test and suite teardowns. In practice this
means that in teardowns all the keywords in all levels are always executed.
When using test templates, all the data rows are always executed to make it sure that all the different combinations are tested. In this usage continuing is limited
to the top-level keywords, and inside them the execution ends normally if there are non-continuable failures.
The tests that are automatically failed get robot:exit tag and the generated report will include NOT robot:exit combined tag pattern to easily see those tests
that were not skipped. Note that the test in which the exit happened does not get the robot:exit tag.
Note
Prior to Robot Framework 3.1, the special tag was named robot-exit.
Pressing Ctrl-C
The execution is stopped when Ctrl-C is pressed in the console where the tests are running. When running the tests on Python, the execution is stopped
immediately, but with Jython it ends only after the currently executing keyword ends.
If Ctrl-C is pressed again, the execution ends immediately and reports and logs are not created.
Using signals
On Unix-like machines it is possible to terminate test execution using signals INT and TERM. These signals can be sent from the command line using kill
command, and sending signals can also be easily automated.
Signals have the same limitation on Jython as pressing Ctrl-C. Similarly also the second signal stops the execution forcefully.
Using keywords
The execution can be stopped also by the executed keywords. There is a separate Fatal Error BuiltIn keyword for this purpose, and custom keywords can use
fatal exceptions when they fail.
If option --exitonfailure (-X) is used, test execution stops immediately if any critical test fails. The remaining tests are marked as failed without actually
executing them.
Note
Robot Framework separates failures caused by failing keywords from errors caused by, for example, invalid settings or failed test library imports. By default
these errors are reported as test execution errors, but errors themselves do not fail tests or affect execution otherwise. If --exitonerror option is used,
however, all such errors are considered fatal and execution stopped so that remaining tests are marked failed. With parsing errors encountered before execution
even starts, this means that no tests are actually run.
Handling teardowns
By default teardowns of the tests and suites that have been started are executed even if the test execution is stopped using one of the methods above. This
allows clean-up activities to be run regardless how execution ends.
It is also possible to skip teardowns when execution is stopped by using --skipteardownonexit option. This can be useful if, for example, clean-up tasks take
a lot of time.
The generic automation mode can also be enabled by using the --rpa option. In that case the executed files can have either tests or tasks. Alternatively --norpa
can be used to force the test automation mode even if executed files contain tasks. If neither of these options are used, it is an error to execute multiple files so
that some have tests and others have tasks.
The execution mode is stored in the generated output file and read by Rebot if outputs are post-processed. The mode can also be set when using Rebot if
necessary.
Synopsis
The most common way to use Rebot is using the rebot runner script. Alternatively it is possible to execute the installed robot.rebot module or robot/rebot.py
file directly using the selected interpreter. The final alternative is using the standalone JAR distribution.
Note
Versions prior to Robot Framework 3.0 installed the rebot script only with Python, and used jyrebot and ipyrebot scripts with Jython and IronPython, respectively.
The old interpreter specific scripts were removed in Robot Framework 3.1 and nowadays rebot must always be used.
The basic syntax for using Rebot is exactly the same as when starting test execution and also most of the command line options are identical. The main
difference is that arguments to Rebot are XML output files instead of test data files or directories.
Return codes from Rebot are exactly same as when running tests.
Rebot notices have tests or tasks been run, and by default preserves the execution mode. The mode affects logs and reports so that in the former case they will
use term test like Test Log and Test Statistics, and in the latter case term task like Task Log and Task Statistics.
Rebot also supports using --rpa or --norpa options to set the execution mode explicitly. This is necessary if multiple output files are processed and they have
conflicting modes.
rebot output.xml
rebot path/to/output_file.xml
rebot --include smoke --name Smoke_Tests c:\results\output.xml
Another common usage is creating only the output file when running tests (log and report generation can be disabled with --log NONE --report NONE) and
generating logs and reports later. Tests can, for example, be executed on different environments, output files collected to a central place, and reports and logs
created there. This approach can also work very well if generating reports and logs takes a lot of time when running tests on Jython. Disabling log and report
generation and generating them later with Rebot can save a lot of time and use less memory.
When outputs are combined, a new top-level test suite is created so that test suites in the given output files are its child suites. This works the same way when
multiple test data files or directories are executed, and also in this case the name of the top-level test suite is created by joining child suite names with an
ampersand (&) and spaces. These automatically generated names are not that good, and it is often a good idea to use --name to give a more meaningful name:
How merging works in practice is explained in the following sections discussing its two main use cases.
There is often a need to re-execute a subset of tests, for example, after fixing a bug in the system under test or in the tests themselves. This can be accomplished
by selecting test cases by names (--test and --suite options), tags (--include and --exclude), or by previous status (--rerunfailed or
--rerunfailedsuites).
Combining re-execution results with the original results using the default combining outputs approach does not work too well. The main problem is that you get
separate test suites and possibly already fixed failures are also shown. In this situation it is better to use --merge (-R) option to tell Rebot to merge the results
instead. In practice this means that tests from the latter test runs replace tests in the original. The usage is best illustrated by a practical example using
--rerunfailed and --merge together:
The message of the merged tests contains a note that results have been replaced. The message also shows the old status and message of the test.
Merged results must always have same top-level test suite. Tests and suites in merged outputs that are not found from the original output are added into the
resulting output. How this works in practice is discussed in the next section.
Another important use case for the --merge option is merging results got when running a test suite in pieces using, for example, --include and --exclude
options:
robot --include smoke --output smoke.xml tests # first run some tests
robot --exclude smoke --output others.xml tests # then run others
rebot --merge smoke.xml others.xml # finally merge results
When merging outputs like this, the resulting output contains all tests and suites found from all given output files. If some test is found from multiple outputs,
latest results replace the earlier ones like explained in the previous section. Also this merging strategy requires the top-level test suites to be same in all outputs.
The --extension option takes a file extension as an argument, and only files with that extension are parsed. If there is a need to parse more than one kind of
files, it is possible to use a colon : to separate extensions. Matching extensions is case insensitive.
If files in one format use different extensions like *.rst and *.rest, you need to specify those extensions separately. Using just one of them would mean that
other files in that format are skipped.
Test suites and test cases can be selected by their names with the command line options --suite (-s) and --test (-t), respectively. Both of these options
can be used several times to select several test suites or cases. Arguments to these options are case- and space-insensitive, and there can also be simple patterns
matching multiple names. If both the --suite and --test options are used, only test cases in matching suites with matching names are selected.
--test Example
--test mytest --test yourtest
--test example*
--test mysuite.mytest
--test *.suite.mytest
--suite example-??
--suite mysuite --test mytest --test your*
Using the --suite option is more or less the same as executing only the appropriate test case file or directory. One major benefit is the possibility to select the
suite based on its parent suite. The syntax for this is specifying both the parent and child suite names separated with a dot. In this case, the possible setup and
teardown of the parent suite are executed.
--suite parent.child
--suite myhouse.myhousemusic --test jack*
Selecting individual test cases with the --test option is very practical when creating test cases, but quite limited when running tests automatically. The
--suite option can be useful in that case, but in general, selecting test cases by tag names is more flexible.
When executing tasks, it is possible to use the --task option as an alias for --test.
By tag names
It is possible to include and exclude test cases by tag names with the --include (-i) and --exclude (-e) options, respectively. If the --include option is
used, only test cases having a matching tag are selected, and with the --exclude option test cases having a matching tag are not. If both are used, only tests
with a tag matching the former option, and not with a tag matching the latter, are selected.
--include example
--exclude not_ready
--include regression --exclude long_lasting
Both --include and --exclude can be used several times to match multiple tags. In that case a test is selected if it has a tag that matches any included tags,
and also has no tag that matches any excluded tags.
In addition to specifying a tag to match fully, it is possible to use tag patterns where * and ? are wildcards and AND, OR, and NOT operators can be used for
combining individual tags or patterns together:
--include feature-4?
--exclude bug*
--include fooANDbar
--exclude xxORyyORzz
--include fooNOTbar
Selecting test cases by tags is a very flexible mechanism and allows many interesting possibilities:
A subset of tests to be executed before other tests, often called smoke tests, can be tagged with smoke and executed with --include smoke.
Unfinished test can be committed to version control with a tag such as not_ready and excluded from the test execution with --exclude not_ready.
Tests can be tagged with sprint-<num>, where <num> specifies the number of the current sprint, and after executing all test cases, a separate report
containing only the tests for a certain sprint can be generated (for example, rebot --include sprint-42 output.xml).
Command line option --rerunfailed (-R) can be used to select all failed tests from an earlier output file for re-execution. This option is useful, for example,
if running all tests takes a lot of time and one wants to iteratively fix failing test cases.
Behind the scenes this option selects the failed tests as they would have been selected individually with the --test option. It is possible to further fine-tune the
list of selected tests by using --test, --suite, --include and --exclude options.
Using an output not originating from executing the same tests that are run now causes undefined results. Additionally, it is an error if the output contains no
failed tests. Using a special value NONE as the output is same as not specifying this option at all.
Tip
Re-execution results and original results can be merged together using the --merge command line option.
Command line option --rerunfailedsuites (-S) can be used to select all failed suites from an earlier output file for re-execution. Like
--rerunfailed (-R), this option is useful when full test execution takes a lot of time. Note that all tests from a failed test suite will be re-executed, even
passing ones. This option is useful when the tests in a test suite depends on each other.
Behind the scenes this option selects the failed suites as they would have been selected individually with the --suite option. It is possible to further fine-tune
the list of selected tests by using --test, --suite, --include and --exclude options.
Note
By default when no tests match the selection criteria test execution fails with an error like:
[ ERROR ] Suite 'Example' with includes 'xxx' contains no test cases.
Because no outputs are generated, this behavior can be problematic if tests are executed and results processed automatically. Luckily a command line option
--RunEmptySuite can be used to force the suite to be executed also in this case. As a result normal outputs are created but show zero executed tests. The same
option can be used also to alter the behavior when an empty directory or a test case file containing no tests is executed.
Similar situation can occur also when processing output files with Rebot. It is possible that no test match the used filtering criteria or that the output file
contained no tests to begin with. By default executing Rebot fails in these cases, but it has a separate --ProcessEmptySuite option that can be used to alter the
behavior. In practice this option works the same way as --RunEmptySuite when running tests.
All test cases are considered critical by default, but this can be changed with the --critical (-c) and --noncritical (-n) options. These options specify
which tests are critical based on tags, similarly as --include and --exclude are used to select tests by tags. If only --critical is used, test cases with a
matching tag are critical. If only --noncritical is used, tests without a matching tag are critical. Finally, if both are used, only test with a critical tag but
without a non-critical tag are critical.
Both --critical and --noncritical also support same tag patterns as --include and --exclude. This means that pattern matching is case, space, and
underscore insensitive, * and ? are supported as wildcards, and AND, OR and NOT operators can be used to create combined patterns.
--critical regression
--noncritical not_ready
--critical iter-* --critical req-* --noncritical req-6??
The most common use case for setting criticality is having test cases that are not ready or test features still under development in the test execution. These tests
could also be excluded from the test execution altogether with the --exclude option, but including them as non-critical tests enables you to see when they start
to pass.
Criticality set when tests are executed is not stored anywhere. If you want to keep same criticality when post-processing outputs with Rebot, you need to use
--critical and/or --noncritical also with it:
# Use rebot to create new log and report from the output created during execution
robot --critical regression --outputdir all tests.robot
rebot --name Smoke --include smoke --critical regression --outputdir smoke all/output.xml
When Robot Framework parses test data, test suite names are created from file and directory names. The name of the top-level test suite can, however, be
overridden with the command line option --name (-N).
Note
Prior to Robot Framework 3.1, underscores in the value were converted to spaces. Nowadays values containing spaces need to be escaped or quoted like, for example, --
name "My example".
In addition to defining documentation in the test data, documentation of the top-level suite can be given from the command line with the option --doc (-D)
The value can contain simple HTML formatting.
Note
Prior to Robot Framework 3.1, underscores in the value were converted to spaces same way as with the --name option.
Free test suite metadata may also be given from the command line with the option --metadata (-M). The argument must be in the format name:value, where
name the name of the metadata to set and value is its value. The value can contain simple HTML formatting. This option may be used several times to set
multiple metadata values.
Note
Prior to Robot Framework 3.1, underscores in the value were converted to spaces same way as with the --name option.
Setting tags
The command line option --settag (-G) can be used to set the given tag to all executed test cases. This option may be used several times to set multiple tags.
Robot Framework uses Python's module search path also when importing resource and variable files if the specified path does not match any file directly.
The module search path being set correctly so that libraries and other extensions are found is a requirement for successful test execution. If you need to
customize it using approaches explained below, it is often a good idea to create a custom start-up script.
Python interpreters have their own standard library as well as a directory where third party modules are installed automatically in the module search path. This
means that test libraries packaged using Python's own packaging system are automatically installed so that they can be imported without any additional
configuration.
Python, Jython and IronPython read additional locations to be added to the module search path from PYTHONPATH, JYTHONPATH and IRONPYTHONPATH
environment variables, respectively. If you want to specify more than one location in any of them, you need to separate the locations with a colon on UNIX-like
machines (e.g. /opt/libs:$HOME/testlibs) and with a semicolon on Windows (e.g. D:\libs;%HOMEPATH%\testlibs).
Environment variables can be configured permanently system wide or so that they affect only a certain user. Alternatively they can be set temporarily before
running a command, something that works extremely well in custom start-up scripts.
Note
Prior to Robot Framework 2.9, contents of PYTHONPATH environment variable were added to the module search path by the framework itself when running on Jython and
IronPython. Nowadays that is not done anymore and JYTHONPATH and IRONPYTHONPATH must be used with these interpreters.
Robot Framework has a separate command line option --pythonpath (-P) for adding locations to the module search path. Although the option name has the
word Python in it, it works also on Jython and IronPython.
Multiple locations can be given by separating them with a colon, regardless the operating system, or by using this option several times. The given path can also
be a glob pattern matching multiple paths, but then it typically needs to be escaped when used on the console.
Examples:
--pythonpath libs
--pythonpath /opt/testlibs:mylibs.zip:yourlibs
--pythonpath mylib.jar --pythonpath lib/\*.jar # '*' is escaped
Python interpreters store the module search path they use as a list of strings in sys.path attribute. This list can be updated dynamically during execution, and
changes are taken into account next time when something is imported.
Java classpath
When libraries implemented in Java are imported with Jython, they can be either in Jython's normal module search path or in Java classpath. The most common
way to alter classpath is setting the CLASSPATH environment variable similarly as PYTHONPATH, JYTHONPATH or IRONPYTHONPATH. Alternatively it is possible to
use Java's -cp command line option. This option is not exposed to the robot runner script, but it is possible to use it with Jython by adding -J prefix like
jython -J-cp example.jar -m robot.run tests.robot.
When using the standalone JAR distribution, the classpath has to be set a bit differently, due to the fact that java -jar command does support the CLASSPATH
environment variable nor the -cp option. There are two different ways to configure the classpath:
--variable name:value
--variable OS:Linux --variable IP:10.0.0.42
--variablefile path/to/variables.py
--variablefile myvars.py:possible:arguments:here
--variable ENVIRONMENT:Windows --variablefile c:\resources\windows.py
In addition to these failures, normal execution errors are shown, for example, when test library or resource file imports cannot be resolved.
It is possible to disable dry run validation of specific user keywords by adding a special robot:no-dry-run keyword tag to them. This is useful if a keyword
fails in the dry run mode for some reason, but work fine when executed normally. Disabling the dry run more is a new feature in Robot Framework 3.0.2.
Note
tests
Test cases inside each test suite are executed in random order.
suites
All test suites are executed in a random order, but test cases inside suites are run in the order they are defined.
all
Both test cases and test suites are executed in a random order.
none
Neither execution order of test nor suites is randomized. This value can be used to override the earlier value set with --randomize.
It is possible to give a custom seed to initialize the random generator. This is useful if you want to re-run tests using the same order as earlier. The seed is given
as part of the value for --randomize in format <what>:<seed> and it must be an integer. If no seed is given, it is generated randomly. The executed top level
test suite automatically gets metadata named Randomized that tells both what was randomized and what seed was used.
Examples:
Pre-run modifiers should be implemented as visitors that can traverse through the executable test suite structure and modify it as needed. The visitor interface is
explained as part of the Robot Framework API documentation, and it possible to modify executed test suites, test cases and keywords using it. The examples
below ought to give an idea of how pre-run modifiers can be used and how powerful this functionality is.
When a pre-run modifier is taken into use on the command line using the --prerunmodifier option, it can be specified either as a name of the modifier class or
a path to the modifier file. If the modifier is given as a class name, the module containing the class must be in the module search path, and if the module name is
different than the class name, the given name must include both like module.ModifierClass. If the modifier is given as a path, the class name must be same as
the file name. For most parts this works exactly like when importing a test library.
If a modifier requires arguments, like the examples below do, they can be specified after the modifier name or path using either a colon (:) or a semicolon (;) as
a separator. If both are used in the value, the one first is considered to be the actual separator.
If more than one pre-run modifier is needed, they can be specified by using the --prerunmodifier option multiple times. If similar modifying is needed before
creating logs and reports, programmatic modification of results can be enabled using the --prerebotmodifier option.
The first example shows how a pre-run-modifier can remove tests from the executed test suite structure. In this example only every Xth tests is preserved, and
the X is given from the command line along with an optional start index.
"""Pre-run modifier that selects only every Xth test for execution.
Starts from the first test by default. Tests are selected per suite.
"""
class SelectEveryXthTest(SuiteVisitor):
If the above pre-run modifier is in a file SelectEveryXthTest.py and the file is in the module search path, it could be used like this:
# Specify the modifier as a name. Run every third test, starting from the second.
robot --prerunmodifier SelectEveryXthTest:3:1 tests.robot
Also the second example removes tests, this time based on a given name pattern. In practice it works like a negative version of the built-in --test option.
Tests to exclude are specified by using a pattern that is both case and space
insensitive and supports '*' (match anything) and '?' (match single character)
as wildcards.
"""
class ExcludeTests(SuiteVisitor):
Assuming the above modifier is in a file named ExcludeTests.py, it could be used like this:
Sometimes when debugging tests it can be useful to disable setups or teardowns. This can be accomplished by editing the test data, but pre-run modifiers make
it easy to do that temporarily for a single run:
"""Pre-run modifiers for disabling suite and test setups and teardowns."""
class SuiteSetup(SuiteVisitor):
class SuiteTeardown(SuiteVisitor):
class TestSetup(SuiteVisitor):
class TestTeardown(SuiteVisitor):
Assuming that the above modifiers are all in a file named disable.py and this file is in the module search path, setups and teardowns could be disabled, for
example, as follows:
The overall console output type is set with the --console option. It supports the following case-insensitive values:
verbose
Every test suite and test case is reported individually. This is the default.
dotted
Only show . for passed test, f for failed non-critical tests, F for failed critical tests, and x for tests which are skipped because test execution exit. Failed
critical tests are listed separately after execution. This output type makes it easy to see are there any failures during execution even if there would be a lot
of tests.
quiet
No output except for errors and warnings.
none
No output whatsoever. Useful when creating a custom output using, for example, listeners.
Separate convenience options --dotted (-.) and --quiet are shortcuts for --console dotted and --console quiet, respectively.
Examples:
--console, --dotted and --quiet are new options in Robot Framework 2.9. Prior to that the output was always the same as in the current verbose mode.
Console width
The width of the test execution output in the console can be set using the option --consolewidth (-W). The default width is 78 characters.
Tip
On many UNIX-like machines you can use handy $COLUMNS environment variable like --consolewidth $COLUMNS.
Note
Prior to Robot Framework 2.9 this functionality was enabled with --monitorwidth option that was first deprecated and is nowadays removed. The short option -W works
the same way in all versions.
Console colors
The --consolecolors (-C) option is used to control whether colors should be used in the console output. Colors are implemented using ANSI colors except
on Windows where, by default, Windows APIs are used instead. Accessing these APIs from Jython is not possible, and as a result colors do not work with
Jython on Windows.
auto
Colors are enabled when outputs are written into the console, but not when they are redirected into a file or elsewhere. This is the default.
on
Colors are used also when outputs are redirected. Does not work on Windows.
ansi
Same as on but uses ANSI colors also on Windows. Useful, for example, when redirecting output to a program that understands ANSI colors.
off
Colors are disabled.
Note
Prior to Robot Framework 2.9 this functionality was enabled with --monitorcolors option that was first deprecated and is nowadays removed. The short option -C
works the same way in all versions.
Console markers
Special markers . (success) and F (failure) are shown on the console when using the verbose output and top level keywords in test cases end. The markers allow
following the test execution in high level, and they are erased when test cases end.
It is possible to configure when markers are used with --consolemarkers (-K) option. It supports the following case-insensitive values:
auto
Markers are enabled when the standard output is written into the console, but not when it is redirected into a file or elsewhere. This is the default.
on
Markers are always used.
off
Markers are disabled.
Note
Prior to Robot Framework 2.9 this functionality was enabled with --monitormarkers option that was first deprecated and is nowadays removed. The short option -K
works the same way in all versions.
Output directory
All output files can be set using an absolute path, in which case they are created to the specified place, but in other cases, the path is considered relative to the
output directory. The default output directory is the directory where the execution is started from, but it can be altered with the --outputdir (-d) option. The
path set with this option is, again, relative to the execution directory, but can naturally be given also as an absolute path. Regardless of how a path to an
individual output file is obtained, its parent directory is created automatically, if it does not exist already.
Output file
Output files contain all the test execution results in machine readable XML format. Log, report and xUnit files are typically generated based on them, and they
can also be combined and otherwise post-processed with Rebot.
Tip
Generating report and xUnit files as part of test execution does not require processing output files. Disabling log generation when running tests can thus save memory.
The command line option --output (-o) determines the path where the output file is created relative to the output directory. The default name for the output
file, when tests are run, is output.xml.
When post-processing outputs with Rebot, new output files are not created unless the --output option is explicitly used.
It is possible to disable creation of the output file when running tests by giving a special value NONE to the --output option. If no outputs are needed, they
should all be explicitly disabled using --output NONE --report NONE --log NONE.
Log file
Log files contain details about the executed test cases in HTML format. They have a hierarchical structure showing test suite, test case and keyword details.
Log files are needed nearly every time when test results are to be investigated in detail. Even though log files also have statistics, reports are better for getting
an higher-level overview.
The command line option --log (-l) determines where log files are created. Unless the special value NONE is used, log files are always created and their
default name is log.html.
Report file
Report files contain an overview of the test execution results in HTML format. They have statistics based on tags and executed test suites, as well as a list of all
executed test cases. When both reports and logs are generated, the report has links to the log file for easy navigation to more detailed information. It is easy to
see the overall test execution status from report, because its background color is green, if all critical tests pass, and bright red otherwise.
The command line option --report (-r) determines where report files are created. Similarly as log files, reports are always created unless NONE is used as a
value, and their default name is report.html.
An example report file of successful test execution
XUnit result files contain the test execution summary in xUnit compatible XML format. These files can thus be used as an input for external tools that
understand xUnit reports. For example, Jenkins continuous integration server supports generating statistics based on xUnit compatible results.
Tip
XUnit output files are not created unless the command line option --xunit (-x) is used explicitly. This option requires a path to the generated xUnit file,
relatively to the output directory, as a value.
Because xUnit reports do not have the concept of non-critical tests, all tests in an xUnit report will be marked either passed or failed, with no distinction
between critical and non-critical tests. If this is a problem, --xunitskipnoncritical option can be used to mark non-critical tests as skipped. Skipped tests
will get a message containing the actual status and possible message of the test case in a format like FAIL: Error message.
Debug file
Debug files are plain text files that are written during the test execution. All messages got from test libraries are written to them, as well as information about
started and ended test suites, test cases and keywords. Debug files can be used for monitoring the test execution. This can be done using, for example, a separate
fileviewer.py tool, or in UNIX-like systems, simply with the tail -f command.
Debug files are not created unless the command line option --debugfile (-b) is used explicitly.
Timestamping output files
All output files listed in this section can be automatically timestamped with the option --timestampoutputs (-T). When this option is used, a timestamp in the
format YYYYMMDD-hhmmss is placed between the extension and the base name of each file. The example below would, for example, create such output files as
output-20080604-163225.xml and mylog-20080604-163225.html:
Setting titles
The default titles for logs and reports are generated by prefixing the name of the top-level test suite with Test Log or Test Report. Custom titles can be given
from the command line using the options --logtitle and --reporttitle, respectively.
Example:
robot --logtitle "Smoke Test Log" --reporttitle "Smoke Test Report" --include smoke my_tests/
Note
Prior to Robot Framework 3.1, underscores in the given titles were converted to spaces. Nowadays spaces need to be escaped or quoted like in the example above.
By default the report file has a green background when all the critical tests pass and a red background otherwise. These colors can be customized by using the
--reportbackground command line option, which takes two or three colors separated with a colon as an argument:
--reportbackground blue:red
--reportbackground green:yellow:red
--reportbackground #00E:#E00
If you specify two colors, the first one will be used instead of the default green color and the second instead of the default red. This allows, for example, using
blue instead of green to make backgrounds easier to separate for color blind people.
If you specify three colors, the first one will be used when all the test succeed, the second when only non-critical tests have failed, and the last when there are
critical failures. This feature thus allows using a separate background color, for example yellow, when non-critical tests have failed.
The specified colors are used as a value for the body element's background CSS property. The value is used as-is and can be a HTML color name (e.g. red), a
hexadecimal value (e.g. #f00 or #ff0000), or an RGB value (e.g. rgb(255,0,0)). The default green and red colors are specified using hexadecimal values #9e9
and #f66, respectively.
Messages in log files can have different log levels. Some of the messages are written by Robot Framework itself, but also executed keywords can log
information using different levels. The available log levels are:
FAIL
Used when a keyword fails. Can be used only by Robot Framework itself.
WARN
Used to display warnings. They shown also in the console and in the Test Execution Errors section in log files, but they do not affect the test case status.
INFO
The default level for normal messages. By default, messages below this level are not shown in the log file.
DEBUG
Used for debugging purposes. Useful, for example, for logging what libraries are doing internally. When a keyword fails, a traceback showing where in
the code the failure occurred is logged using this level automatically.
TRACE
More detailed debugging level. The keyword arguments and return values are automatically logged using this level.
By default, log messages below the INFO level are not logged, but this threshold can be changed from the command line using the --loglevel (-L) option.
This option takes any of the available log levels as an argument, and that level becomes the new threshold level. A special value NONE can also be used to
disable logging altogether.
It is possible to use the --loglevel option also when post-processing outputs with Rebot. This allows, for example, running tests initially with the TRACE level,
and generating smaller log files for normal viewing later with the INFO level. By default all the messages included during execution will be included also with
Rebot. Messages ignored during the execution cannot be recovered.
Another possibility to change the log level is using the BuiltIn keyword Set Log Level in the test data. It takes the same arguments as the --loglevel option,
and it also returns the old level so that it can be restored later, for example, in a test teardown.
If the log file contains messages at DEBUG or TRACE levels, a visible log level drop down is shown in the upper right corner. This allows users to remove
messages below chosen level from the view. This can be useful especially when running test at TRACE level.
By default the drop down will be set at the lowest level in the log file, so that all messages are shown. The default visible log level can be changed using
--loglevel option by giving the default after the normal log level separated by a colon:
--loglevel DEBUG:INFO
In the above example, tests are run using level DEBUG, but the default visible level in the log file is INFO.
The main benefit of splitting logs is that individual log parts are so small that opening and browsing the log file is possible even if the amount of the test data is
very large. A small drawback is that the overall size taken by the log file increases.
Technically the test data related to each test case is saved into a JavaScript file in the same folder as the main log file. These files have names such as log-42.js
where log is the base name of the main log file and 42 is an incremented index.
Note
When copying the log files, you need to copy also all the log-*.js files or some information will be missing.
When a deeper suite structure is executed, showing all the test suite levels in the Statistics by Suite table may make the table somewhat difficult to read. By
default all suites are shown, but you can control this with the command line option --suitestatlevel which takes the level of suites to show as an argument:
--suitestatlevel 3
When many tags are used, the Statistics by Tag table can become quite congested. If this happens, the command line options --tagstatinclude and
--tagstatexclude can be used to select which tags to display, similarly as --include and --exclude are used to select test cases:
--tagstatinclude some-tag --tagstatinclude another-tag
--tagstatexclude owner-*
--tagstatinclude prefix-* --tagstatexclude prefix-13
The command line option --tagstatcombine can be used to generate aggregate tags that combine statistics from multiple tags. The combined tags are specified
using tag patterns where * and ? are supported as wildcards and AND, OR and NOT operators can be used for combining individual tags or patterns together.
The following examples illustrate creating combined tag statistics using different patterns, and the figure below shows a snippet of the resulting Statistics by
Tag table:
--tagstatcombine owner-*
--tagstatcombine smokeANDmytag
--tagstatcombine smokeNOTowner-janne*
As the above example illustrates, the name of the added combined statistic is, by default, just the given pattern. If this is not good enough, it is possible to give
a custom name after the pattern by separating them with a colon (:):
Note
Prior to Robot Framework 3.1, underscores in the custom name were converted to spaces. Nowadays spaces need to be escaped or quoted like in the example above.
You can add external links to the Statistics by Tag table by using the command line option --tagstatlink. Arguments to this option are given in the format
tag:link:name, where tag specifies the tags to assign the link to, link is the link to be created, and name is the name to give to the link.
tag may be a single tag, but more commonly a simple pattern where * matches anything and ? matches any single character. When tag is a pattern, the matches
to wildcards may be used in link and title with the syntax %N, where "N" is the index of the match starting from 1.
The following examples illustrate the usage of this option, and the figure below shows a snippet of the resulting Statistics by Tag table when example test data is
executed with these options:
--tagstatlink mytag:http://www.google.com:Google
--tagstatlink jython-bug-*:http://bugs.jython.org/issue_%1:Jython-bugs
--tagstatlink owner-*:mailto:%[email protected]?subject=Acceptance_Tests:Send_Mail
Tags can be given a documentation with the command line option --tagdoc, which takes an argument in the format tag:doc. tag is the name of the tag to
assign the documentation to, and it can also be a simple pattern matching multiple tags. doc is the assigned documentation. It can contain simple HTML
formatting.
The given documentation is shown with matching tags in the Test Details by Tag table, and as a tool tip for these tags in the Statistics by Tag table. If one tag
gets multiple documentations, they are combined together and separated with an ampersand.
Examples:
--tagdoc mytag:Example
--tagdoc "regression:*See* http://info.html"
--tagdoc "owner-*:Original author"
Note
Prior to Robot Framework 3.1, underscores in the documentation were converted to spaces. Nowadays spaces need to be escaped or quoted like in the examples above.
In these situations, command line options --removekeywords and --flattenkeywords can be used to dispose or flatten unnecessary keywords. They can be
used both when executing test cases and when post-processing outputs. When used during execution, they only affect the log file, not the XML output file. With
rebot they affect both logs and possibly generated new output XML files.
Removing keywords
The --removekeywords option removes keywords and their messages altogether. It has the following modes of operation, and it can be used multiple times to
enable multiple modes. Keywords that contain errors or warnings are not removed except when using the ALL mode.
ALL
Remove data from all keywords unconditionally.
PASSED
Remove keyword data from passed test cases. In most cases, log files created using this option contain enough information to investigate possible
failures.
FOR
Remove all passed iterations from for loops except the last one.
WUKS
Remove all failing keywords inside BuiltIn keyword Wait Until Keyword Succeeds except the last one.
NAME:<pattern>
Remove data from all keywords matching the given pattern regardless the keyword status. The pattern is matched against the full name of the keyword,
prefixed with the possible library or resource file name. The pattern is case, space, and underscore insensitive, and it supports simple patterns with * and ?
as wildcards.
TAG:<pattern>
Remove data from keywords with tags that match the given pattern. Tags are case and space insensitive and they can be specified using tag patterns
where * and ? are supported as wildcards and AND, OR and NOT operators can be used for combining individual tags or patterns together. Can be used both
with library keyword tags and user keyword tags.
Examples:
Removing keywords is done after parsing the output file and generating an internal model based on it. Thus it does not reduce memory usage as much as
flattening keywords.
Note
Flattening keywords
The --flattenkeywords option flattens matching keywords. In practice this means that matching keywords get all log messages from their child keywords,
recursively, and child keywords are discarded otherwise. Flattening supports the following modes:
FOR
Flatten for loops fully.
FORITEM
Flatten individual for loop iterations.
NAME:<pattern>
Flatten keywords matching the given pattern. Pattern matching rules are same as when removing keywords using NAME:<pattern> mode.
TAG:<pattern>
Flatten keywords with tags matching the given pattern. Pattern matching rules are same as when removing keywords using TAG:<pattern> mode.
Examples:
Flattening keywords is done already when the output file is parsed initially. This can save a significant amount of memory especially with deeply nested
keyword structures.
Note
It is also possible to use the above mentioned options to set start and end times for a single suite when using Rebot. Using these options with a single output
always affects the elapsed time of the suite.
Times must be given as timestamps in the format YYYY-MM-DD hh:mm:ss.mil, where all separators are optional and the parts from milliseconds to hours can be
omitted. For example, 2008-06-11 17:59:20.495 is equivalent both to 20080611-175920.495 and 20080611175920495, and also mere 20080611 would work.
Examples:
Full error messages are always visible in log files as messages of the failed keywords.
Note
This functionality works nearly exactly like programmatic modification of test data that can be enabled with the --prerunmodifier option. The obvious
difference is that this time modifiers operate with the result model, not the running model. For example, the following modifier marks all passed tests that have
taken more time than allowed as failed:
class ExecutionTimeChecker(SuiteVisitor):
If the above modifier would be in file ExecutionTimeChecker.py, it could be used, for example, like this:
# Specify modifier as a name when using Rebot. Maximum time is 3.14 seconds.
# ExecutionTimeChecker.py must be in the module search path.
rebot --prerebotmodifier ExecutionTimeChecker:3.14 output.xml
If more than one model modifier is needed, they can be specified by using the --prerebotmodifier option multiple times. When executing tests, it is possible
to use --prerunmodifier and --prerebotmodifier options together.
A system log has the same log levels as a normal log file, with the exception that instead of FAIL it has the ERROR level. The threshold level to use can be altered
using the ROBOT_SYSLOG_LEVEL environment variable like shown in the example below. Possible unexpected errors and warnings are written into the system log
in addition to the console and the normal log file.
#!/bin/bash
export ROBOT_SYSLOG_FILE=/tmp/syslog.txt
export ROBOT_SYSLOG_LEVEL=DEBUG
4.1.1 Introduction
Supported programming languages
Different test library APIs
4.1.2 Creating test library class or module
Test library names
Providing arguments to test libraries
Test library scope
Specifying library version
Specifying documentation format
Library acting as listener
4.1.3 Creating static keywords
What methods are considered keywords
Keyword names
Keyword tags
Keyword arguments
Default values to keywords
Variable number of arguments (*varargs)
Free keyword arguments (**kwargs)
Keyword-only arguments
Argument types
Using decorators
Embedding arguments into keyword names
4.1.4 Communicating with Robot Framework
Reporting keyword status
Stopping test execution
Continuing test execution despite of failures
Logging information
Programmatic logging APIs
Logging during library initialization
Returning values
Communication when using threads
4.1.5 Distributing test libraries
Documenting libraries
Testing libraries
Packaging libraries
Deprecating keywords
4.1.6 Dynamic library API
Getting keyword names
Running keywords
Getting keyword arguments
Getting keyword argument types
Getting keyword tags
Getting keyword documentation
Getting general library documentation
Named argument syntax with dynamic libraries
Free named arguments with dynamic libraries
Named-only arguments with dynamic libraries
Summary
4.1.7 Hybrid library API
Getting keyword names
Running keywords
Getting keyword arguments and documentation
Summary
4.1.8 Using Robot Framework's internal modules
Available APIs
Using BuiltIn library
4.1.9 Extending existing test libraries
Modifying original source code
Using inheritance
Using other libraries directly
Getting active library instance from Robot Framework
Libraries using dynamic or hybrid API
4.1.1 Introduction
Robot Framework itself is written with Python and naturally test libraries extending it can be implemented using the same language. When running the
framework on Jython, libraries can also be implemented using Java. Pure Python code works both on Python and Jython, assuming that it does not use syntax
or modules that are not available on Jython. When using Python, it is also possible to implement libraries with C using Python C API, although it is often easier
to interact with C code from Python libraries using ctypes module.
Libraries implemented using these natively supported languages can also act as wrappers to functionality implemented using other programming languages. A
good example of this approach is the Remote library, and another widely used approaches is running external scripts or tools as separate processes.
Static API
The simplest approach is having a module (in Python) or a class (in Python or Java) with methods which map directly to keyword names.
Keywords also take the same arguments as the methods implementing them. Keywords report failures with exceptions, log by writing to standard
output and can return values using the return statement.
Dynamic API
Dynamic libraries are classes that implement a method to get the names of the keywords they implement, and another method to execute a named
keyword with given arguments. The names of the keywords to implement, as well as how they are executed, can be determined dynamically at
runtime, but reporting the status, logging and returning values is done similarly as in the static API.
Hybrid API
This is a hybrid between the static and the dynamic API. Libraries are classes with a method telling what keywords they implement, but those
keywords must be available directly. Everything else except discovering what keywords are implemented is similar as in the static API.
All these APIs are described in this chapter. Everything is based on how the static API works, so its functions are discussed first. How the dynamic library API
and the hybrid library API differ from it is then discussed in sections of their own.
The examples in this chapter are mainly about using Python, but they should be easy to understand also for Java-only developers. In those few cases where
APIs have differences, both usages are explained with adequate examples.
The name of a test library that is used when a library is imported is the same as the name of the module or class implementing it. For example, if you have a
Python module MyLibrary (that is, file MyLibrary.py), it will create a library with name MyLibrary. Similarly, a Java class YourLibrary, when it is not in any
package, creates a library with exactly that name.
Python classes are always inside a module. If the name of a class implementing a library is the same as the name of the module, Robot Framework allows
dropping the class name when importing the library. For example, class MyLib in MyLib.py file can be used as a library with just name MyLib. This also works
with submodules so that if, for example, parent.MyLib module has class MyLib, importing it using just parent.MyLib works. If the module name and class
name are different, libraries must be taken into use using both module and class names, such as mymodule.MyLibrary or parent.submodule.MyLib.
Java classes in a non-default package must be taken into use with the full name. For example, class MyLib in com.mycompany.myproject package must be
imported with name com.mycompany.myproject.MyLib.
Tip
If the library name is really long, for example when the Java package name is long, it is recommended to give the library a simpler alias by using the WITH NAME
syntax.
All test libraries implemented as classes can take arguments. These arguments are specified in the Setting table after the library name, and when Robot
Framework creates an instance of the imported library, it passes them to its constructor. Libraries implemented as a module cannot take any arguments, so
trying to use those results in an error.
The number of arguments needed by the library is the same as the number of arguments accepted by the library's constructor. The default values and variable
number of arguments work similarly as with keyword arguments, with the exception that there is no variable argument support for Java libraries. Arguments
passed to the library, as well as the library name itself, can be specified using variables, so it is possible to alter them, for example, from the command line.
Example implementations, first one in Python and second in Java, for the libraries used in the above example:
class MyLibrary:
Libraries implemented as classes can have an internal state, which can be altered by keywords and with arguments to the constructor of the library. Because the
state can affect how keywords actually behave, it is important to make sure that changes in one test case do not accidentally affect other test cases. These kind
of dependencies may create hard-to-debug problems, for example, when new test cases are added and they use the library inconsistently.
Robot Framework attempts to keep test cases independent from each other: by default, it creates new instances of test libraries for every test case. However, this
behavior is not always desirable, because sometimes test cases should be able to share a common state. Additionally, all libraries do not have a state and
creating new instances of them is simply not needed.
Test libraries can control when new libraries are created with a class attribute ROBOT_LIBRARY_SCOPE . This attribute must be a string and it can have the
following three values:
TEST CASE
A new instance is created for every test case. A possible suite setup and suite teardown share yet another instance. This is the default.
TEST SUITE
A new instance is created for every test suite. The lowest-level test suites, created from test case files and containing test cases, have instances of their
own, and higher-level suites all get their own instances for their possible setups and teardowns.
GLOBAL
Only one instance is created during the whole test execution and it is shared by all test cases and test suites. Libraries created from modules are always
global.
Note
If a library is imported multiple times with different arguments, a new instance is created every time regardless the scope.
When the TEST SUITE or GLOBAL scopes are used with test libraries that have a state, it is recommended that libraries have some special keyword for cleaning
up the state. This keyword can then be used, for example, in a suite setup or teardown to ensure that test cases in the next test suites can start from a known
state. For example, SeleniumLibrary uses the GLOBAL scope to enable using the same browser in different test cases without having to reopen it, and it also has
the Close All Browsers keyword for easily closing all opened browsers.
class ExampleLibrary:
def __init__(self):
self._counter = 0
def count(self):
self._counter += 1
print(self._counter)
def clear_counter(self):
self._counter = 0
When a test library is taken into use, Robot Framework tries to determine its version. This information is then written into the syslog to provide debugging
information. Library documentation tool Libdoc also writes this information into the keyword documentations it generates.
Version information is read from attribute ROBOT_LIBRARY_VERSION, similarly as test library scope is read from ROBOT_LIBRARY_SCOPE. If
ROBOT_LIBRARY_VERSION does not exist, information is tried to be read from __version__ attribute. These attributes must be class or module attributes,
depending whether the library is implemented as a class or a module. For Java libraries the version attribute must be declared as static final.
__version__ = '0.1'
def keyword():
pass
Library documentation tool Libdoc supports documentation in multiple formats. If you want to use something else than Robot Framework's own documentation
formatting, you can specify the format in the source code using ROBOT_LIBRARY_DOC_FORMAT attribute similarly as scope and version are set with their own
ROBOT_LIBRARY_* attributes.
The possible case-insensitive values for documentation format are ROBOT (default), HTML, TEXT (plain text), and reST (reStructuredText). Using the reST format
requires the docutils module to be installed when documentation is generated.
Setting the documentation format is illustrated by the following Python and Java examples that use reStructuredText and HTML formats, respectively. See
Documenting libraries section and Libdoc chapter for more information about documenting test libraries in general.
__ http://docutils.sourceforge.net
"""
ROBOT_LIBRARY_DOC_FORMAT = 'reST'
def keyword():
"""**Nothing** to see here. Not even in the table below.
/**
* A library for <i>documentation format</i> demonstration purposes.
*
* This documentation is created using <a href="http://www.w3.org/html">HTML</a>.
* Here is a link to the only `Keyword`.
*/
public class DocFormatExample {
Listener interface allows external listeners to get notifications about test execution. They are called, for example, when suites, tests, and keywords start and end.
Sometimes getting such notifications is also useful for test libraries, and they can register a custom listener by using ROBOT_LIBRARY_LISTENER attribute. The
value of this attribute should be an instance of the listener to use, possibly the library itself. For more information and examples see Test libraries as listeners
section.
When the static library API is used, Robot Framework uses reflection to find out what public methods the library class or module contains. It will exclude all
methods starting with an underscore (unless using a custom keyword name), and with Java libraries also methods implemented only in the implicit base class
java.lang.Object are excluded. All the methods that are not ignored are considered keywords. For example, the Python and Java libraries below implement a
single keyword My Keyword.
class MyLibrary:
When implementing a library as a Python or Java class, also methods in possible base classes are considered keywords. When implementing a library as a
Python module, also possible functions imported into the module namespace become keywords. For example, if the module below would be used as a library, it
would contain keywords Example Keyword, Second Example and also Current Thread.
def example_keyword():
print('Running in thread "%s".' % current_thread().name)
def second_example():
pass
A simple way to avoid imported functions becoming keywords is to only import modules (e.g. import threading) and use functions via the module (e.g
threading.current_thread()). Alternatively functions could be given an alias starting with an underscore at the import time (e.g. from threading import
current_thread as _current_thread).
A more explicit way to limit what functions become keywords is using the module level __all__ attribute that Python itself uses for similar purposes. If it is
used, only the listed functions can be keywords. For example, the library below implements only keywords Example Keyword and Second Example.
def example_keyword():
print('Running in thread "%s".' % current_thread().name)
def second_example():
pass
def not_exposed_as_keyword():
pass
Keyword names
Keyword names used in the test data are compared with method names to find the method implementing these keywords. Name comparison is case-insensitive,
and also spaces and underscores are ignored. For example, the method hello maps to the keyword name Hello, hello or even h e l l o. Similarly both the
do_nothing and doNothing methods can be used as the Do Nothing keyword in the test data.
def hello(name):
print("Hello, %s!" % name)
def do_nothing():
pass
The example below illustrates how the example libraries above can be used. If you want to try this yourself, make sure that the library is in the module search
path.
It is possible to expose a different name for a keyword instead of the default keyword name which maps to the method name. This can be accomplished by
setting the robot_name attribute on the method to the desired custom name. This is typically easiest done by using the robot.api.deco.keyword decorator as
follows:
Using this decorator without an argument will have no effect on the exposed keyword name, but will still set the robot_name attribute. This allows marking
methods to expose as keywords without actually changing keyword names. Starting from Robot Framework 3.0.2, methods that have the robot_name attribute
also create keywords even if the method name itself would start with an underscore.
Setting a custom keyword name can also enable library keywords to accept arguments using Embedded Arguments syntax.
Keyword tags
Starting from Robot Framework 2.9, library keywords and user keywords can have tags. Library keywords can define them by setting the robot_tags attribute
on the method to a list of desired tags. The robot.api.deco.keyword decorator may be used as a shortcut for setting this attribute when used as follows:
@keyword(tags=['tag1', 'tag2'])
def login(username, password):
# ...
Another option for setting tags is giving them on the last line of keyword documentation with Tags: prefix and separated by a comma. For example:
Keyword arguments
With a static and hybrid API, the information on how many arguments a keyword needs is got directly from the method that implements it. Libraries using the
dynamic library API have other means for sharing this information, so this section is not relevant to them.
The most common and also the simplest situation is when a keyword needs an exact number of arguments. In this case, both the Python and Java methods
simply take exactly those arguments. For example, a method implementing a keyword with no arguments takes no arguments either, a method implementing a
keyword with one argument also takes one argument, and so on.
def no_arguments():
print("Keyword got no arguments.")
def one_argument(arg):
print("Keyword got one argument '%s'." % arg)
Note
A major limitation with Java libraries using the static library API is that they do not support the named argument syntax. If this is a blocker, it is possible to either use
Python or switch to the dynamic library API.
It is often useful that some of the arguments that a keyword uses have default values. Python and Java have different syntax for handling default values to
methods, and the natural syntax of these languages can be used when creating test libraries for Robot Framework.
In Python a method has always exactly one implementation and possible default values are specified in the method signature. The syntax, which is familiar to
all Python programmers, is illustrated below:
def one_default(arg='default'):
print("Argument has value %s" % arg)
def multiple_defaults(arg1, arg2='default 1', arg3='default 2'):
print("Got arguments %s, %s and %s" % (arg1, arg2, arg3))
The first example keyword above can be used either with zero or one arguments. If no arguments are given, arg gets the value default. If there is one
argument, arg gets that value, and calling the keyword with more than one argument fails. In the second example, one argument is always required, but the
second and the third one have default values, so it is possible to use the keyword with one to three arguments.
In Java one method can have several implementations with different signatures. Robot Framework regards all these implementations as one keyword, which
can be used with different arguments. This syntax can thus be used to provide support for the default values. This is illustrated by the example below, which is
functionally identical to the earlier Python example:
Robot Framework supports also keywords that take any number of arguments. Similarly as with the default values, the actual syntax to use in test libraries is
different in Python and Java.
Python supports methods accepting any number of arguments. The same syntax works in libraries and, as the examples below show, it can also be combined
with other ways of specifying arguments:
def any_arguments(*args):
print("Got arguments:")
for arg in args:
print(arg)
Robot Framework supports Java varargs syntax for defining variable number of arguments. For example, the following two keywords are functionally identical
to the above Python examples with same names:
It is also possible to use variable number of arguments also by having an array or java.util.List as the last argument, or second to last if free keyword
arguments (**kwargs) are used. This is illustrated by the following examples that are functionally identical to the previous ones:
Note
The support for variable number of arguments with Java keywords has one limitation: it works only when methods have one signature. Thus it is not possible to
have Java keywords with both default values and varargs.
Robot Framework supports Python's **kwargs syntax and extends that support also to Java. How to use use keywords that accept free keyword arguments, also
known as free named arguments, is discussed under the Creating test cases section. In this section we take a look at how to create such keywords using Python
and Java.
If you are already familiar how kwargs work with Python, understanding how they work with Robot Framework test libraries is rather simple. The example
below shows the basic functionality:
def example_keyword(**stuff):
for name, value in stuff.items():
print(name, value)
Basically, all arguments at the end of the keyword call that use the named argument syntax name=value, and that do not match any other arguments, are passed
to the keyword as kwargs. To avoid using a literal value like foo=quux as a free keyword argument, it must be escaped like foo\=quux.
The following example illustrates how normal arguments, varargs, and kwargs work together:
Named
Various Args arg=value # Logs 'arg: value'.
Kwargs
Various Args a=1 b=2 c=3 # Logs 'kwarg: a 1', 'kwarg: b 2' and 'kwarg: c 3'.
Various Args c=3 a=1 b=2 # Same as above. Order does not matter.
For a real world example of using a signature exactly like in the above example, see Run Process and Start Keyword keywords in the Process library.
Also Java libraries support the free keyword arguments syntax. Java itself has no kwargs syntax, but keywords can have java.util.Map as the last argument to
specify that they accept kwargs.
If a Java keyword accepts kwargs, Robot Framework will automatically pack all arguments in name=value syntax at the end of the keyword call into a Map and
pass it to the keyword. For example, following example keywords can be used exactly like the previous Python examples:
Note
The type of the kwargs argument must be exactly java.util.Map, not any of its sub types.
Note
Similarly as with the varargs support, a keyword supporting kwargs cannot have more than one signature.
Keyword-only arguments
Starting from Robot Framework 3.1, it is possible to use named-only arguments with different keywords. When implementing libraries using Python, this
support is provided by Python's keyword-only arguments:
Due to keyword-only arguments being a Python 3 feature, libraries using Python 2 cannot use it. Time to upgrade!
Argument types
Arguments defined in Robot Framework test data are, by default, passed to keywords as Unicode strings. There are, however, several ways to use non-string
values as well:
Variables can contain any kind of objects as values, and variables used as arguments are passed to keywords as-is.
Keywords can themselves convert arguments they accept to other types.
It is possible to specify argument types explicitly using Python 3 function annotations or the @keyword decorator. In these cases Robot Framework
converts arguments automatically.
Automatic conversion is also done based on keyword default values.
Arguments to Java keywords are converted based on argument type information.
Automatic argument conversion based on function annotations, types specified using the @keyword decorator, and argument default values are all new features
in Robot Framework 3.1. The Supported conversions section specifies which argument conversion are supported in these cases.
It is always possible to convert arguments passed as strings insider keywords. In simple cases this means using int() or float() to convert arguments to
numbers, but other kind of conversion is possible as well. When working with Boolean values, care must be taken because all non-empty strings, including
string False, are considered true by Python. Robot Framework's own robot.utils.is_truthy() utility handles this nicely as it considers strings like FALSE,
NO and NONE (case-insensitively) to be false:
Notice that with Robot Framework 3.1 and newer is_truthy is not needed in the above example because argument type would be got based on the default
value.
Starting from Robot Framework 3.1, arguments passed to keywords as strings are automatically converted if argument type information is available. The most
natural way to specify types is using Python 3 function annotations. For example, the keyword in the previous example could be implemented as follows and
arguments would be converted automatically:
See the Supported conversions section below for a list of types that are automatically converted and what values these types accept. It is an error if an argument
having one of the supported types is given a value that cannot be converted. Annotating only some of the arguments is fine.
Annotating arguments with other than the supported types is not an error, and it is also possible to use annotations for other than typing purposes. In those cases
no conversion is done, but annotations are nevertheless shown in the documentation generated by Libdoc.
Note
Because function annotations are a Python 3 feature, using them in a library that should also work with Python 2 is not possible.
Note
Using function annotations with Robot Framework 3.0.2 or earlier is not possible at all.
An alternative way to specify explicit argument types is using the robot.api.deco.keyword decorator. Starting from Robot Framework 3.1, it accepts an
optional types argument that can be used to specify argument types either as a dictionary mapping argument names to types or as a list mapping arguments to
types based on position. These approaches are shown below implementing the same keyword as in earlier examples:
@keyword(types=[int, bool])
def example_keyword(count, case_insensitive=True):
if case_insensitive:
# ...
Regardless of the approach that is used, it is not necessarily to specify types for all arguments. When specifying types as a list, it is possible to use None or any
other non-true value to mark that a certain argument does not have a type, and arguments at the end can be omitted altogether. For example, both of these
keywords specify the type only for the second argument:
@keyword(types={'second': float})
def example1(first, second, third):
# ...
@keyword(types=[None, float])
def example2(first, second, third):
# ...
If any types are specified using the @keyword decorator, type information got from annotations is ignored with that keyword. Setting types to None like
@keyword(types=None) disables type conversion altogether so that also type information got from default values is ignored.
Implicit argument types based on default values
If type information is not got explicitly using annotations or the @keyword decorator, Robot Framework 3.1 and newer tries to get it based on possible argument
default value. In this example count and case_insensitive get types int and bool, respectively:
When type information is got implicitly based on the default values, argument conversion itself is not as strict as when the information is got explicitly:
Conversion may be attempted also to other "similar" types. For example, if converting to an integer fails, float conversion is attempted.
Conversion failures are not errors, keywords get the original value in these cases instead.
If argument conversion based on default values is not desired with a certain argument, it can be disabled by specifying a type for that argument explicitly.
Alternatively argument conversion can be disabled altogether with the @keyword decorator like @keyword(types=None).
Supported conversions
The table below lists the types that Robot Framework 3.1 and newer convert arguments to. These characteristics apply to all conversions:
Type can be explicitly specified using function annotations or the @keyword decorator.
If not explicitly specified, type can be got implicitly from argument default values.
Conversion is done only if the argument that is used is itself a Unicode string.
With most of the types failed conversion causes an error if the type has been specified explicitly. Exceptions to this rule are mentioned in the table below.
With most of the types string NONE, case-insensitively, is converted to Python None. Exceptions are mentioned in the table below.
The type to use can be specified either using concrete types (e.g. list), by using Abstract Base Classes (ABC) (e.g. Sequence), or by using sub classes of these
types (e.g. MutableSequence). In all these cases the argument is converted to the concrete type.
Also types in in the typing module that map to the supported concrete types or ABCs (e.g. List) are supported. With generics also the subscription syntax (e.g.
List[int]) works, but no validation is done for container contents.
In addition to using the actual types (e.g. int), it is possible to specify the type using type names as a string (e.g. 'int') and some types also have aliases (e.g.
'integer'). Matching types to names and aliases is case-insensitive.
GREEN
NoneType String NONE (case-insensitively) is converted to None object, other values are None
passed as-is. Mainly relevant when type is got implicitly from None being a
default value.
list Sequence Argument must be be a Python list literal. It is converted to an actual list ['foo', 'bar']
using the ast.literal_eval function. The list can contain any values [('one', 1), ('two', 2)]
ast.literal_eval supports inside it, including other lists or other containers.
tuple Same as list but the argument must be a tuple literal. ('foo', 'bar')
dict Mapping dictionary, Same as list but the argument must be a dictionary literal. {'a': 1, 'b': 2}
map {'key': 1, 'nested': {'key': 2}}
set Set Same as list but the argument must be a set literal or set() to create an {1, 2, 3, 42}
empty set. Not supported on Python 2. set()
Arguments to Java methods have types, and all the base types are handled automatically. This means that arguments that are normal strings in the test data are
coerced to correct type at runtime. The types that can be coerced are:
The coercion is done for arguments that have the same or compatible type across all the signatures of the keyword method. In the following example, the
conversion can be done for keywords doubleArgument and compatibleTypes, but not for conflictingTypes.
The coercion works with the numeric types if the test data has a string containing a number, and with the boolean type the data must contain either string true
or false. Coercion is only done if the original value was a string from the test data, but it is of course still possible to use variables containing correct types
with these keywords. Using variables is the only option if keywords have conflicting signatures.
No Coercion
Double Argument ${3.14}
Conflicting Types 1 ${2} # must use variables
Conflicting Types ${1} 2
Note
Converting arguments passed to Java based keywords is an old feature and independent on the support to convert arguments of Python keywords in Robot Framework 3.1
and newer. Conversion functionality may be unified in the future.
Using decorators
When writing static keywords, it is sometimes useful to modify them with Python's decorators. However, decorators modify function signatures, and can
confuse Robot Framework's introspection when determining which arguments keywords accept. This is especially problematic when creating library
documentation with Libdoc and when using RIDE. To avoid this issue, either do not use decorators, or use the handy decorator module to create signature-
preserving decorators.
Library keywords can also accept arguments which are passed using Embedded Argument syntax. The robot.api.deco.keyword decorator can be used to
create a custom keyword name for the keyword which includes the desired syntax.
By default arguments are passed to implementing keywords as strings, but automatic argument type conversion works if type information is specified somehow.
With Python 3 it is convenient to use function annotations, and alternatively it is possible to pass types to the @keyword decorator:
Note
Reporting keyword status is done simply using exceptions. If an executed method raises an exception, the keyword status is FAIL, and if it returns normally, the
status is PASS.
The error message shown in logs, reports and the console is created from the exception type and its message. With generic exceptions (for example,
AssertionError, Exception, and RuntimeError), only the exception message is used, and with others, the message is created in the format ExceptionType:
Actual message.
It is possible to avoid adding the exception type as a prefix to failure message also with non generic exceptions. This is done by adding a special
ROBOT_SUPPRESS_NAME attribute with value True to your exception.
Python:
class MyError(RuntimeError):
ROBOT_SUPPRESS_NAME = True
Java:
In all cases, it is important for the users that the exception message is as informative as possible.
It is also possible to have HTML formatted error messages by starting the message with text *HTML*:
This method can be used both when raising an exception in a library, like in the example above, and when users provide an error message in the test data.
If the error message is longer than 40 lines, it will be automatically cut from the middle to prevent reports from getting too long and difficult to read. The full
error message is always shown in the log message of the failed keyword.
Tracebacks
The traceback of the exception is also logged using DEBUG log level. These messages are not visible in log files by default because they are very rarely
interesting for normal users. When developing libraries, it is often a good idea to run tests using --loglevel DEBUG.
It is possible to fail a test case so that the whole test execution is stopped. This is done simply by having a special ROBOT_EXIT_ON_FAILURE attribute with True
value set on the exception raised from the keyword. This is illustrated in the examples below.
Python:
class MyFatalError(RuntimeError):
ROBOT_EXIT_ON_FAILURE = True
Java:
It is possible to continue test execution even when there are failures. The way to signal this from test libraries is adding a special ROBOT_CONTINUE_ON_FAILURE
attribute with True value to the exception used to communicate the failure. This is demonstrated by the examples below.
Python:
class MyContinuableError(RuntimeError):
ROBOT_CONTINUE_ON_FAILURE = True
Java:
Logging information
Exception messages are not the only way to give information to the users. In addition to them, methods can also send messages to log files simply by writing to
the standard output stream (stdout) or to the standard error stream (stderr), and they can even use different log levels. Another, and often better, logging
possibility is using the programmatic logging APIs.
By default, everything written by a method into the standard output is written to the log file as a single entry with the log level INFO. Messages written into the
standard error are handled similarly otherwise, but they are echoed back to the original stderr after the keyword execution has finished. It is thus possible to use
the stderr if you need some messages to be visible on the console where tests are executed.
To use other log levels than INFO, or to create several messages, specify the log level explicitly by embedding the level into the message in the format *LEVEL*
Actual log message, where *LEVEL* must be in the beginning of a line and LEVEL is one of the available logging levels TRACE, DEBUG, INFO, WARN, ERROR and
HTML.
Messages with ERROR or WARN level are automatically written to the console and a separate Test Execution Errors section in the log files. This makes these
messages more visible than others and allows using them for reporting important but non-critical problems to users.
Note
In Robot Framework 2.9, new functionality was added to automatically add ERRORs logged by keywords to the Test Execution Errors section.
Logging HTML
Everything normally logged by the library will be converted into a format that can be safely represented as HTML. For example, <b>foo</b> will be displayed
in the log exactly like that and not as foo. If libraries want to use formatting, links, display images and so on, they can use a special pseudo log level HTML.
Robot Framework will write these messages directly into the log with the INFO level, so they can use any HTML syntax they want. Notice that this feature
needs to be used with care, because, for example, one badly placed </table> tag can ruin the log file quite badly.
When using the public logging API, various logging methods have optional html attribute that can be set to True to enable logging in HTML format.
Timestamps
By default messages logged via the standard output or error streams get their timestamps when the executed keyword ends. This means that the timestamps are
not accurate and debugging problems especially with longer running keywords can be problematic.
Keywords have a possibility to add an accurate timestamp to the messages they log if there is a need. The timestamp must be given as milliseconds since the
Unix epoch and it must be placed after the log level separated from it with a colon:
Python:
import time
def example_keyword():
print('*INFO:%d* Message with timestamp' % (time.time()*1000))
Java:
Logging to console
If libraries need to write something to the console they have several options. As already discussed, warnings and all messages written to the standard error
stream are written both to the log file and to the console. Both of these options have a limitation that the messages end up to the console only after the currently
executing keyword finishes. A bonus is that these approaches work both with Python and Java based libraries.
Another option, that is only available with Python, is writing messages to sys.__stdout__ or sys.__stderr__. When using this approach, messages are
written to the console immediately and are not written to the log file at all:
import sys
def my_keyword(arg):
sys.__stdout__.write('Got arg %s\n' % arg)
def log_to_console(arg):
logger.console('Got arg %s' % arg)
def log_to_console_and_log_file(arg):
logger.info('Got arg %s' % arg, also_console=True)
Logging example
In most cases, the INFO level is adequate. The levels below it, DEBUG and TRACE, are useful for writing debug information. These messages are normally not
shown, but they can facilitate debugging possible problems in the library itself. The WARN or ERROR level can be used to make messages more visible and HTML is
useful if any kind of formatting is needed.
The following examples clarify how logging with different levels works. Java programmers should regard the code print('message') as pseudocode meaning
System.out.println("message");.
Programmatic APIs provide somewhat cleaner way to log information than using the standard output and error streams. Currently these interfaces are available
only to Python bases test libraries.
Public logging API
Robot Framework has a Python based logging API for writing messages to the log file and to the console. Test libraries can use this API like logger.info('My
message') instead of logging through the standard output like print('*INFO* My message'). In addition to a programmatic interface being a lot cleaner to
use, this API has a benefit that the log messages have accurate timestamps.
The public logging API is thoroughly documented as part of the API documentation at https://robot-framework.readthedocs.org. Below is a simple usage
example:
def my_keyword(arg):
logger.debug('Got argument %s' % arg)
do_something()
logger.info('<i>This</i> is a boring example', html=True)
logger.console('Hello, console!')
An obvious limitation is that test libraries using this logging API have a dependency to Robot Framework. If Robot Framework is not running, the messages are
redirected automatically to Python's standard logging module.
In addition to the new public logging API, Robot Framework offers a built-in support to Python's standard logging module. This works so that all messages that
are received by the root logger of the module are automatically propagated to Robot Framework's log file. Also this API produces log messages with accurate
timestamps, but logging HTML messages or writing messages to the console are not supported. A big benefit, illustrated also by the simple example below, is
that using this logging API creates no dependency to Robot Framework.
import logging
def my_keyword(arg):
logging.debug('Got argument %s' % arg)
do_something()
logging.info('This is a boring example')
The logging module has slightly different log levels than Robot Framework. Its levels DEBUG, INFO, WARNING and ERROR are mapped directly to the matching
Robot Framework log levels, and CRITICAL is mapped to ERROR. Custom log levels are mapped to the closest standard level smaller than the custom level. For
example, a level between INFO and WARNING is mapped to Robot Framework's INFO level.
Libraries can also log during the test library import and initialization. These messages do not appear in the log file like the normal log messages, but are instead
written to the syslog. This allows logging any kind of useful debug information about the library initialization. Messages logged using the WARN or ERROR levels
are also visible in the test execution errors section in the log file.
Logging during the import and initialization is possible both using the standard output and error streams and the programmatic logging APIs. Both of these are
demonstrated below.
public LoggingDuringInitialization() {
System.out.println("*INFO* Initializing library");
}
logger.debug("Importing library")
def keyword():
# ...
Note
If you log something during initialization, i.e. in Python __init__ or in Java constructor, the messages may be logged multiple times depending on the test library scope.
Returning values
The final way for keywords to communicate back to the core framework is returning information retrieved from the system under test or generated by some
other means. The returned values can be assigned to variables in the test data and then used as inputs for other keywords, even from different test libraries.
Values are returned using the return statement both from the Python and Java methods. Normally, one value is assigned into one scalar variable, as illustrated
in the example below. This example also illustrates that it is possible to return any objects and to use extended variable syntax to access object attributes.
def return_string():
return "Hello, world!"
def return_object(name):
return MyObject(name)
Keywords can also return values so that they can be assigned into several scalar variables at once, into a list variable, or into scalar variables and a list variable.
All these usages require that returned values are Python lists or tuples or in Java arrays, Lists, or Iterators.
def return_two_values():
return 'first value', 'second value'
def return_multiple_values():
return ['a', 'list', 'of', 'strings']
If a library uses threads, it should generally communicate with the framework only from the main thread. If a worker thread has, for example, a failure to report
or something to log, it should pass the information first to the main thread, which can then use exceptions or other mechanisms explained in this section for
communication with the framework.
This is especially important when threads are run on background while other keywords are running. Results of communicating with the framework in that case
are undefined and can in the worst case cause a crash or a corrupted output file. If a keyword starts something on background, there should be another keyword
that checks the status of the worker thread and reports gathered information accordingly.
Messages logged by non-main threads using the normal logging methods from programmatic logging APIs are silently ignored.
There is also a BackgroundLogger in separate robotbackgroundlogger project, with a similar API as the standard robot.api.logger. Normal logging methods
will ignore messages from other than main thread, but the BackgroundLogger will save the background messages so that they can be later logged to Robot's
log.
Documenting libraries
A test library without documentation about what keywords it contains and what those keywords do is rather useless. To ease maintenance, it is highly
recommended that library documentation is included in the source code and generated from it. Basically, that means using docstrings with Python and Javadoc
with Java, as in the examples below.
class MyLibrary:
"""This is an example library with some documentation."""
def keyword_with_longer_documentation(self):
"""First line of the documentation is here.
/**
* This is an example library with some documentation.
*/
public class MyLibrary {
/**
* This keyword has only a short documentation
*/
public void keywordWithShortDocumentation(String argument) {
}
/**
* First line of the documentation is here.
*
* Longer documentation continues here and it can contain
* multiple lines or paragraphs.
*/
public void keywordWithLongerDocumentation() {
}
Both Python and Java have tools for creating an API documentation of a library documented as above. However, outputs from these tools can be slightly
technical for some users. Another alternative is using Robot Framework's own documentation tool Libdoc. This tool can create a library documentation from
both Python and Java libraries using the static library API, such as the ones above, but it also handles libraries using the dynamic library API and hybrid library
API.
The first logical line of a keyword documentation, until the first empty line, is used for a special purpose and should contain a short overall description of the
keyword. It is used as a short documentation by Libdoc (for example, as a tool tip) and also shown in the test logs. The latter does not work with Java libraries
using the static API, though, because their documentation is not available at runtime.
By default documentation is considered to follow Robot Framework's documentation formatting rules. This simple format allows often used styles like *bold*
and _italic_, tables, lists, links, etc. It is possible to use also HTML, plain text and reStructuredText formats. See Specifying documentation format section for
information how to set the format in the library source code and Libdoc chapter for more information about the formats in general.
Note
Prior to Robot Framework 3.1, the short documentation contained only the first physical line of the keyword documentation.
Note
If you want to use non-ASCII characters in the documentation of Python libraries, you must either use UTF-8 as your source code encoding or create docstrings as
Unicode. When using Python 3, UTF-8 is the default source encoding.
Testing libraries
Any non-trivial test library needs to be thoroughly tested to prevent bugs in them. Of course, this testing should be automated to make it easy to rerun tests
when libraries are changed.
Both Python and Java have excellent unit testing tools, and they suite very well for testing libraries. There are no major differences in using them for this
purpose compared to using them for some other testing. The developers familiar with these tools do not need to learn anything new, and the developers not
familiar with them should learn them anyway.
It is also easy to use Robot Framework itself for testing libraries and that way have actual end-to-end acceptance tests for them. There are plenty of useful
keywords in the BuiltIn library for this purpose. One worth mentioning specifically is Run Keyword And Expect Error, which is useful for testing that keywords
report errors correctly.
Whether to use a unit- or acceptance-level testing approach depends on the context. If there is a need to simulate the actual system under test, it is often easier
on the unit level. On the other hand, acceptance tests ensure that keywords do work through Robot Framework. If you cannot decide, of course it is possible to
use both the approaches.
Packaging libraries
After a library is implemented, documented, and tested, it still needs to be distributed to the users. With simple libraries consisting of a single file, it is often
enough to ask the users to copy that file somewhere and set the module search path accordingly. More complicated libraries should be packaged to make the
installation easier.
Since libraries are normal programming code, they can be packaged using normal packaging tools. For information about packaging and distributing Python
code see https://packaging.python.org/. When such a package is installed using pip or other tools, it is automatically in the module search path.
When using Java, it is natural to package libraries into a JAR archive. The JAR package must be put into the module search path before running tests, but it is
easy to create a start-up script that does that automatically.
Deprecating keywords
Sometimes there is a need to replace existing keywords with new ones or remove them altogether. Just informing the users about the change may not always be
enough, and it is more efficient to get warnings at runtime. To support that, Robot Framework has a capability to mark keywords deprecated. This makes it
easier to find old keywords from the test data and remove or replace them.
Keywords can be deprecated by starting their documentation with text *DEPRECATED, case-sensitive, and having a closing * also on the first line of the
documentation. For example, *DEPRECATED*, *DEPRECATED.*, and *DEPRECATED in version 1.5.* are all valid markers.
When a deprecated keyword is executed, a deprecation warning is logged and the warning is shown also in the console and the Test Execution Errors section in
log files. The deprecation warning starts with text Keyword '<name>' is deprecated. and has rest of the short documentation after the deprecation marker, if
any, afterwards. For example, if the following keyword is executed, there will be a warning like shown below in the log file.
def example_keyword(argument):
"""*DEPRECATED!!* Use keyword `Other Keyword` instead.
This deprecation system works with most test libraries and also with user keywords. The only exception are keywords implemented in a Java test library that
uses the static library interface because their documentation is not available at runtime. With such keywords, it possible to use user keywords as wrappers and
deprecate them.
Note
Prior to Robot Framework 2.9 the documentation must start with *DEPRECATED* exactly without any extra content before the closing *.
Only differences between static and dynamic libraries are how Robot Framework discovers what keywords a library implements, what arguments and
documentation these keywords have, and how the keywords are actually executed. With the static API, all this is done using reflection (except for the
documentation of Java libraries), but dynamic libraries have special methods that are used for these purposes.
One of the benefits of the dynamic API is that you have more flexibility in organizing your library. With the static API, you must have all keywords in one class
or module, whereas with the dynamic API, you can, for example, implement each keyword as a separate class. This use case is not so important with Python,
though, because its dynamic capabilities and multi-inheritance already give plenty of flexibility, and there is also possibility to use the hybrid library API.
Another major use case for the dynamic API is implementing a library so that it works as proxy for an actual library possibly running on some other process or
even on another machine. This kind of a proxy library can be very thin, and because keyword names and all other information is got dynamically, there is no
need to update the proxy when new keywords are added to the actual library.
This section explains how the dynamic API works between Robot Framework and dynamic libraries. It does not matter for Robot Framework how these
libraries are actually implemented (for example, how calls to the run_keyword method are mapped to a correct keyword implementation), and many different
approaches are possible. However, if you use Java, you may want to examine the JavaLibCore project before implementing your own system. This collection of
reusable tools supports several ways of creating keywords, and it is likely that it already has a mechanism that suites your needs. Python users may also find the
similar PythonLibCore project useful.
Dynamic libraries tell what keywords they implement with the get_keyword_names method. The method also has the alias getKeywordNames that is
recommended when using Java. This method cannot take any arguments, and it must return a list or array of strings containing the names of the keywords that
the library implements.
If the returned keyword names contain several words, they can be returned separated with spaces or underscores, or in the camelCase format. For example,
['first keyword', 'second keyword'], ['first_keyword', 'second_keyword'], and ['firstKeyword', 'secondKeyword'] would all be mapped to
keywords First Keyword and Second Keyword.
Dynamic libraries must always have this method. If it is missing, or if calling it fails for some reason, the library is considered a static library.
If a dynamic library should contain both methods which are meant to be keywords and methods which are meant to be private helper methods, it may be wise to
mark the keyword methods as such so it is easier to implement get_keyword_names. The robot.api.deco.keyword decorator allows an easy way to do this
since it creates a custom robot_name attribute on the decorated method. This allows generating the list of keywords just by checking for the robot_name
attribute on every method in the library during get_keyword_names. See Using a custom keyword name for more about this decorator.
class DynamicExample:
def get_keyword_names(self):
return [name for name in dir(self) if hasattr(getattr(self, name), 'robot_name')]
def helper_method(self):
# ...
@keyword
def keyword_method(self):
# ...
Running keywords
Dynamic libraries have a special run_keyword (alias runKeyword) method for executing their keywords. When a keyword from a dynamic library is used in the
test data, Robot Framework uses the run_keyword method to get it executed. This method takes two or three arguments. The first argument is a string
containing the name of the keyword to be executed in the same format as returned by get_keyword_names. The second argument is a list of positional
arguments given to the keyword in the test data, and the optional third argument is a dictionary (map in Java) containing named arguments. If the third
argument is missing, free named arguments and named-only arguments are not supported, and other named arguments are mapped to positional arguments.
Note
Prior to Robot Framework 3.1, normal named arguments were mapped to positional arguments regardless did run_keyword accept two or three arguments. The third
argument only got possible free named arguments.
After getting keyword name and arguments, the library can execute the keyword freely, but it must use the same mechanism to communicate with the
framework as static libraries. This means using exceptions for reporting keyword status, logging by writing to the standard output or by using the provided
logging APIs, and using the return statement in run_keyword for returning something.
Every dynamic library must have both the get_keyword_names and run_keyword methods but rest of the methods in the dynamic API are optional. The
example below shows a working, albeit trivial, dynamic library implemented in Python.
class DynamicExample:
def get_keyword_names(self):
return ['first keyword', 'second keyword']
If a dynamic library only implements the get_keyword_names and run_keyword methods, Robot Framework does not have any information about the
arguments that the implemented keywords accept. For example, both First Keyword and Second Keyword in the example above could be used with any
arguments. This is problematic, because most real keywords expect a certain number of keywords, and under these circumstances they would need to check the
argument counts themselves.
Dynamic libraries can communicate what arguments their keywords expect by using the get_keyword_arguments (alias getKeywordArguments) method. This
method gets the name of a keyword as an argument, and it must return a list of strings containing the arguments accepted by that keyword.
Similarly as other keywords, dynamic keywords can require any number of positional arguments, have default values, accept variable number of arguments,
accept free named arguments and have named-only arguments. The syntax how to represent all these different variables is derived from how they are specified
in Python and explained in the following table. Note that the examples use Python syntax for lists, but Java developers should use Java lists or String arrays
instead.
The actual argument names and default values that are returned are also important. They are needed for named argument support and the Libdoc tool needs
them to be able to create a meaningful library documentation.
If get_keyword_arguments is missing or returns Python None or Java null for a certain keyword, that keyword gets an argument specification accepting all
arguments. This automatic argument spec is either [*varargs, **kwargs] or [*varargs], depending does run_keyword support free named arguments or not.
Robot Framework 3.1 introduced support for automatic argument conversion and the dynamic library API supports that as well. The conversion logic works
exactly like with static libraries, but how the type information is specified is naturally different.
With dynamic libraries types can be returned using the optional get_keyword_types method (alias getKeywordTypes). It can return types using a list or a
dictionary exactly like types can be specified when using the @keyword decorator. Type information can be specified using actual types like int, but especially
if a dynamic library gets this information from external systems, using strings like 'int' or 'integer' may be easier. See the Supported conversions section
for more information about supported types and how to specify them.
Starting from Robot Framework 3.0.2, dynamic libraries can report keyword tags by using the get_keyword_tags method (alias getKeywordTags). It gets a
keyword name as an argument, and should return corresponding tags as a list of strings.
Alternatively it is possible to specify tags on the last row of the documentation returned by the get_keyword_documentation method discussed below. This
requires starting the last row with Tags: and listing tags after it like Tags: first tag, second, third. This approach works also with Robot Framework
versions prior to 3.0.2.
Tip
The get_keyword_tags method is guaranteed to be called before the get_keyword_documentation method. This makes it easy to embed tags into the documentation
only if the get_keyword_tags method is not called.
If dynamic libraries want to provide keyword documentation, they can implement the get_keyword_documentation method (alias
getKeywordDocumentation). It takes a keyword name as an argument and, as the method name implies, returns its documentation as a string.
The returned documentation is used similarly as the keyword documentation string with static libraries implemented with Python. The main use case is getting
keywords' documentations into a library documentation generated by Libdoc. Additionally, the first line of the documentation (until the first \n) is shown in test
logs.
The get_keyword_documentation method can also be used for specifying overall library documentation. This documentation is not used when tests are
executed, but it can make the documentation generated by Libdoc much better.
Dynamic libraries can provide both general library documentation and documentation related to taking the library into use. The former is got by calling
get_keyword_documentation with special value __intro__, and the latter is got using value __init__. How the documentation is presented is best tested with
Libdoc in practice.
Python based dynamic libraries can also specify the general library documentation directly in the code as the docstring of the library class and its __init__
method. If a non-empty documentation is got both directly from the code and from the get_keyword_documentation method, the latter has precedence.
Also the dynamic library API supports the named argument syntax. Using the syntax works based on the argument names and default values got from the
library using the get_keyword_arguments method.
If the run_keyword method accepts three arguments, the second argument gets all positional arguments as a list and the last arguments gets all named
arguments as a mapping. If it accepts only two arguments, named arguments are mapped to positional arguments. In the latter case, if a keyword has multiple
arguments with default values and only some of the latter ones are given, the framework fills the skipped optional arguments based on the default values
returned by the get_keyword_arguments method.
Using the named argument syntax with dynamic libraries is illustrated by the following examples. All the examples use a keyword Dynamic that has an
argument specification [a, b=d1, c=d2]. The comment on each row shows how run_keyword would be called in these cases if it has two arguments (i.e.
signature is name, args) and if it has three arguments (i.e. name, args, kwargs).
*** Test Cases *** # args # args, kwargs
Positional only
Dynamic x # [x] # [x], {}
Dynamic x y # [x, y] # [x, y], {}
Dynamic x y z # [x, y, z] # [x, y, z], {}
Named only
Dynamic a=x # [x] # [], {a: x}
Dynamic c=z a=x b=y # [x, y, z] # [], {a: x, b: y, c: z}
Intermediate missing
Dynamic x c=z # [x, d1, z] # [x], {c: z}
Note
Prior to Robot Framework 3.1, all normal named arguments were mapped to positional arguments and the optional kwargs was only used with free named arguments.
With the above examples run_keyword was always called like it is nowadays called if it does not support kwargs.
Dynamic libraries can also support free named arguments (**named). A mandatory precondition for this support is that the run_keyword method takes three
arguments: the third one will get the free named arguments along with possible other named arguments. These arguments are passed to the keyword as a
mapping.
What arguments a keyword accepts depends on what get_keyword_arguments returns for it. If the last argument starts with **, that keyword is recognized to
accept free named arguments.
Using the free named argument syntax with dynamic libraries is illustrated by the following examples. All the examples use a keyword Dynamic that has an
argument specification [a=d1, b=d2, **named]. The comment shows the arguments that the run_keyword method is actually called with.
Positional only
Dynamic x # [x], {}
Dynamic x y # [x, y], {}
Note
Prior to Robot Framework 3.1, normal named arguments were mapped to positional arguments but nowadays they are part of the kwargs along with the free named
arguments.
Starting from Robot Framework 3.1, dynamic libraries can have named-only arguments. This requires that the run_keyword method takes three arguments: the
third getting the named-only arguments along with the other named arguments.
In the argument specification returned by the get_keyword_arguments method named-only arguments are specified after possible variable number of
arguments (*varargs) or a lone asterisk (*) if the keyword does not accept varargs. Named-only arguments can have default values, and the order of arguments
with and without default values does not matter.
Using the named-only argument syntax with dynamic libraries is illustrated by the following examples. All the examples use a keyword Dynamic that has been
specified to have argument specification [positional=default, *varargs, named, named2=default, **free]. The comment shows the arguments that the
run_keyword method is actually called with.
Summary
All special methods in the dynamic API are listed in the table below. Method names are listed in the underscore format, but their camelCase aliases work
exactly the same way.
It is possible to write a formal interface specification in Java as below. However, remember that libraries do not need to implement any explicit interface,
because Robot Framework directly checks with reflection if the library has the required get_keyword_names and run_keyword methods or their camelCase
aliases.
List<String> getKeywordNames();
Note
In addition to using List, it is possible to use also arrays like Object[] or String[].
A good example of using the dynamic API is Robot Framework's own Remote library.
Keyword names are got in the exactly same way as with the dynamic API. In practice, the library needs to have the get_keyword_names or getKeywordNames
method returning a list of keyword names that the library implements.
Running keywords
In the hybrid API, there is no run_keyword method for executing keywords. Instead, Robot Framework uses reflection to find methods implementing keywords,
similarly as with the static API. A library using the hybrid API can either have those methods implemented directly or, more importantly, it can handle them
dynamically.
In Python, it is easy to handle missing methods dynamically with the __getattr__ method. This special method is probably familiar to most Python
programmers and they can immediately understand the following example. Others may find it easier to consult Python Reference Manual first.
from somewhere import external_keyword
class HybridExample:
def get_keyword_names(self):
return ['my_keyword', 'external_keyword']
Note that __getattr__ does not execute the actual keyword like run_keyword does with the dynamic API. Instead, it only returns a callable object that is then
executed by Robot Framework.
Another point to be noted is that Robot Framework uses the same names that are returned from get_keyword_names for finding the methods implementing
them. Thus the names of the methods that are implemented in the class itself must be returned in the same format as they are defined. For example, the library
above would not work correctly, if get_keyword_names returned My Keyword instead of my_keyword.
The hybrid API is not very useful with Java, because it is not possible to handle missing methods with it. Of course, it is possible to implement all the methods
in the library class, but that brings few benefits compared to the static API.
When this API is used, Robot Framework uses reflection to find the methods implementing keywords, similarly as with the static API. After getting a reference
to the method, it searches for arguments and documentation from it, in the same way as when using the static API. Thus there is no need for special methods for
getting arguments and documentation like there is with the dynamic API.
Summary
When implementing a test library in Python, the hybrid API has the same dynamic capabilities as the actual dynamic API. A great benefit with it is that there is
no need to have special methods for getting keyword arguments and documentation. It is also often practical that the only real dynamic keywords need to be
handled in __getattr__ and others can be implemented directly in the main library class.
Because of the clear benefits and equal capabilities, the hybrid API is in most cases a better alternative than the dynamic API when using Python. One notable
exception is implementing a library as a proxy for an actual library implementation elsewhere, because then the actual keyword must be executed elsewhere
and the proxy can only pass forward the keyword name and arguments.
A good example of using the hybrid API is Robot Framework's own Telnet library.
Available APIs
API documentation is hosted separately at the excellent Read the Docs service. If you are unsure how to use certain API or is using them forward compatible,
please send a question to mailing list.
The safest API to use are methods implementing keywords in the BuiltIn library. Changes to keywords are rare and they are always done so that old usage is
first deprecated. One of the most useful methods is replace_variables which allows accessing currently available variables. The following example
demonstrates how to get ${OUTPUT_DIR} which is one of the many handy automatic variables. It is also possible to set new variables from libraries using
set_test_variable, set_suite_variable and set_global_variable.
import os.path
from robot.libraries.BuiltIn import BuiltIn
def do_something(argument):
output = do_something_that_creates_a_lot_of_output(argument)
outputdir = BuiltIn().replace_variables('${OUTPUTDIR}')
path = os.path.join(outputdir, 'results.txt')
f = open(path, 'w')
f.write(output)
f.close()
print('*HTML* Output written to <a href="results.txt">results.txt</a>')
The only catch with using methods from BuiltIn is that all run_keyword method variants must be handled specially. Methods that use run_keyword methods
have to be registered as run keywords themselves using register_run_keyword method in BuiltIn module. This method's documentation explains why this
needs to be done and obviously also how to do it.
If you have access to the source code of the library you want to extend, you can naturally modify the source code directly. The biggest problem of this approach
is that it can be hard for you to update the original library without affecting your changes. For users it may also be confusing to use a library that has different
functionality than the original one. Repackaging the library may also be a big extra task.
This approach works extremely well if the enhancements are generic and you plan to submit them back to the original developers. If your changes are applied
to the original library, they are included in the future releases and all the problems discussed above are mitigated. If changes are non-generic, or you for some
other reason cannot submit them back, the approaches explained in the subsequent sections probably work better.
Using inheritance
Another straightforward way to extend an existing library is using inheritance. This is illustrated by the example below that adds new Title Should Start With
keyword to the SeleniumLibrary. This example uses Python, but you can obviously extend an existing Java library in Java code the same way.
class ExtendedSeleniumLibrary(SeleniumLibrary):
A big difference with this approach compared to modifying the original library is that the new library has a different name than the original. A benefit is that
you can easily tell that you are using a custom library, but a big problem is that you cannot easily use the new library with the original. First of all your new
library will have same keywords as the original meaning that there is always conflict. Another problem is that the libraries do not share their state.
This approach works well when you start to use a new library and want to add custom enhancements to it from the beginning. Otherwise other mechanisms
explained in this section are probably better.
Because test libraries are technically just classes or modules, a simple way to use another library is importing it and using its methods. This approach works
great when the methods are static and do not depend on the library state. This is illustrated by the earlier example that uses Robot Framework's BuiltIn library.
If the library has state, however, things may not work as you would hope. The library instance you use in your library will not be the same as the framework
uses, and thus changes done by executed keywords are not visible to your library. The next section explains how to get an access to the same library instance
that the framework uses.
BuiltIn keyword Get Library Instance can be used to get the currently active library instance from the framework itself. The library instance returned by this
keyword is the same as the framework itself uses, and thus there is no problem seeing the correct library state. Although this functionality is available as a
keyword, it is typically used in test libraries directly by importing the BuiltIn library class as discussed earlier. The following example illustrates how to
implement the same Title Should Start With keyword as in the earlier example about using inheritance.
def title_should_start_with(expected):
seleniumlib = BuiltIn().get_library_instance('SeleniumLibrary')
title = seleniumlib.get_title()
if not title.startswith(expected):
raise AssertionError("Title '%s' did not start with '%s'"
% (title, expected))
This approach is clearly better than importing the library directly and using it when the library has a state. The biggest benefit over inheritance is that you can
use the original library normally and use the new library in addition to it when needed. That is demonstrated in the example below where the code from the
previous examples is expected to be available in a new library SeLibExtensions.
Test libraries that use the dynamic or hybrid library API often have their own systems how to extend them. With these libraries you need to ask guidance from
the library developers or consult the library documentation or source code.
4.2.1 Introduction
4.2.2 Putting Remote library to use
Importing Remote library
Starting and stopping remote servers
4.2.3 Supported argument and return value types
4.2.4 Remote protocol
Required methods
Getting remote keyword names and other information
Executing remote keywords
Different argument syntaxes
4.2.1 Introduction
There are two main reasons for using the remote library API:
It is possible to have actual libraries on different machines than where Robot Framework is running. This allows interesting possibilities for distributed
testing.
Test libraries can be implemented using any language that supports XML-RPC protocol. There exists ready-made generic remote servers for various
languages like Python, Java, Ruby, .NET, and so on.
The remote library interface is provided by the Remote library that is one of the standard libraries. This library does not have any keywords of its own, but it
works as a proxy between the core framework and keywords implemented elsewhere. The Remote library interacts with actual library implementations through
remote servers, and the Remote library and servers communicate using a simple remote protocol on top of an XML-RPC channel. The high level architecture of
all this is illustrated in the picture below:
Note
The remote client uses Python's standard XML-RPC module. It does not support custom XML-RPC extensions implemented by some XML-RPC servers.
The Remote library needs to know the address of the remote server but otherwise importing it and using keywords that it provides is no different to how other
libraries are used. If you need to use the Remote library multiple times in a test suite, or just want to give it a more descriptive name, you can import it using the
WITH NAME syntax.
*** Settings ***
Library Remote http://127.0.0.1:8270 WITH NAME Example1
Library Remote http://example.com:8080/ WITH NAME Example2
Library Remote http://10.0.0.2/example 1 minute WITH NAME Example3
The URL used by the first example above is also the default address that the Remote library uses if no address is given.
The last example above shows how to give a custom timeout to the Remote library as an optional second argument. The timeout is used when initially
connecting to the server and if a connection accidentally closes. Timeout can be given in Robot Framework time format like 60s or 2 minutes 10 seconds.
The default timeout is typically several minutes, but it depends on the operating system and its configuration. Notice that setting a timeout that is shorter than
keyword execution time will interrupt the keyword. Setting a custom timeout does not work with IronPython.
Note
Port 8270 is the default port that remote servers are expected to use and it has been registered by IANA for this purpose. This port number was selected because 82 and
70 are the ASCII codes of letters R and F, respectively.
Note
When connecting to the local machine, it is recommended to use IP address 127.0.0.1 instead of machine name localhost. This avoids address resolution that can be
extremely slow at least on Windows.
Note
If the URI contains no path after the server address, the XML-RPC module used by the Remote library will use /RPC2 path by default. In practice using
http://127.0.0.1:8270 is thus identical to using http://127.0.0.1:8270/RPC2. Depending on the remote server this may or may not be a problem. No extra path
is appended if the address has a path even if the path is just /. For example, neither http://127.0.0.1:8270/ nor http://127.0.0.1:8270/my/path will be
modified.
Before the Remote library can be imported, the remote server providing the actual keywords must be started. If the server is started before launching the test
execution, it is possible to use the normal Library setting like in the above example. Alternatively other keywords, for example from Process or SSH libraries,
can start the server up, but then you may need to use Import Library keyword because the library is not available when the test execution starts.
How a remote server can be stopped depends on how it is implemented. Typically servers support the following methods:
Regardless of the library used, remote servers should provide Stop Remote Server keyword that can be easily used by executed tests.
Remote servers should have stop_remote_server method in their XML-RPC interface.
Hitting Ctrl-C on the console where the server is running should stop the server.
The server process can be terminated using tools provided by the operating system (e.g. kill).
Note
Servers may be configured so that users cannot stop it with Stop Remote Server keyword or stop_remote_server method.
Both the Remote library and the Python remote server handle Python values according to the following rules. Other remote servers should behave similarly.
The remote protocol is implemented on top of XML-RPC, which is a simple remote procedure call protocol using XML over HTTP. Most mainstream
languages (Python, Java, C, Ruby, Perl, Javascript, PHP, ...) have a support for XML-RPC either built-in or as an extension.
Required methods
A remote server is an XML-RPC server that must have the same methods in its public interface as the dynamic library API has. Only get_keyword_names and
run_keyword are actually required, but get_keyword_arguments, get_keyword_types, get_keyword_tags and get_keyword_documentation are also
recommended. Notice that using the camelCase format like getKeywordNames in method names is not possible similarly as in the normal dynamic API. How
the actual keywords are implemented is not relevant for the Remote library. Remote servers can either act as wrappers for the real test libraries, like the
available generic remote servers do, or they can implement keywords themselves.
Remote servers should additionally have stop_remote_server method in their public interface to ease stopping them. They should also automatically expose
this method as Stop Remote Server keyword to allow using it in the test data regardless of the test library. Allowing users to stop the server is not always
desirable, and servers may support disabling this functionality somehow. The method, and also the exposed keyword, should return True or False depending
on whether stopping is allowed or not. That makes it possible for external tools to know if stopping the server succeeded.
The Remote library gets the list of keywords that a remote server provides by using the get_keyword_names method. Remote servers must implement this
method and the method must return keyword names as a list of strings.
Remote servers can, and should, also implement get_keyword_arguments, get_keyword_types, get_keyword_tags and get_keyword_documentation
methods to provide more information about the keywords. All these methods take the name of the keyword as an argument. Arguments must be returned as a
list of strings in the same format as with dynamic libraries, tags as a list of strings, and documentation as a string.
Type information can be returned either as a list mapping type names to arguments based on position or as a dictionary mapping argument names to type names
directly. In practice this works the same way as when specifying types using the @keyword decorator with normal libraries. The difference is that because the
XML-RPC protocol does not support arbitrary values, type information needs to be specified using type names or aliases like 'int' or 'integer', not using
actual types like int. Additionally None or null values may not be allowed, and the empty string should be used instead if a marker telling certain argument
does not have type information is needed.
Remote servers can also provide general library documentation to be used when generating documentation with the Libdoc tool.
Note
get_keyword_tags is new in Robot Framework 3.0.2. With earlier versions keyword tags can be embedded into the keyword documentation.
Note
When the Remote library wants the server to execute some keyword, it calls the remote server's run_keyword method and passes it the keyword name, a list of
arguments, and possibly a dictionary of free named arguments. Base types can be used as arguments directly, but more complex types are converted to
supported types.
The server must return results of the execution in a result dictionary (or map, depending on terminology) containing items explained in the following table.
Notice that only the status entry is mandatory, others can be omitted if they are not applicable.
Also free named arguments (**kwargs) works mostly the same way as with other dynamic libraries. First of all, the get_keyword_arguments must return an
argument specification that contains **kwargs exactly like with any other dynamic library. The main difference is that remote servers' run_keyword method
must have an optional third argument that gets the kwargs specified by the user. The third argument must be optional because, for backwards-compatibility
reasons, the Remote library passes kwargs to the run_keyword method only when they have been used in the test data.
In practice run_keyword should look something like the following Python and Java examples, depending on how the language handles optional arguments.
Listeners are classes or modules with certain special methods, and they can be implemented both with Python and Java. Listeners that monitor the whole test
execution must be taken into use from the command line. In addition to that, test libraries can register listeners that receive notifications while that library is
active.
It is also possible to give arguments to listener classes from the command line. Arguments are specified after the listener name (or path) using a colon (:) as a
separator. If a listener is given as an absolute Windows path, the colon after the drive letter is not considered a separator. Additionally it is possible to use a
semicolon (;) as an alternative argument separator. This is useful if listener arguments themselves contain colons, but requires surrounding the whole value
with quotes on UNIX-like operating systems:
The main difference between listener versions 2 and 3 is that the former only gets information about the execution but cannot directly affect it. The latter
interface gets data and result objects Robot Framework itself uses and is thus able to alter execution and change results. See listener examples for more
information about what listeners can do.
Another difference between versions 2 and 3 is that the former supports both Python and Java but the latter supports only Python.
Listener versions 2 and 3 have mostly the same methods, but the arguments they accept are different. These methods and their arguments are explained in the
following sections. All methods that have an underscore in their name have also camelCase alternative. For example, start_suite method can be used also
with name startSuite.
Listener version 2
Listener methods in the API version 2 are listed in the following table. All methods related to test execution progress have the same signature method(name,
attributes), where attributes is a dictionary containing details of the event. Listener methods are free to do whatever they want to do with the information
they receive, but they cannot directly change it. If that is needed, listener version 3 can be used instead.
id: Suite id. s1 for the top level suite, s1-s1 for its first child suite, s1-s2 for the second child, and so
on.
longname: Suite name including parent suites.
doc: Suite documentation.
metadata: Free test suite metadata as a dictionary/map.
source: An absolute path of the file/directory the suite was created from.
suites: Names of the direct child suites this suite has as a list.
tests: Names of the tests this suite has as a list. Does not include tests of the possible child suites.
totaltests: The total number of tests in this suite. and all its sub-suites as an integer.
starttime: Suite execution start time.
id: Test id in format like s1-s2-t2, where the beginning is the parent suite id and the last part shows
test index in that suite.
longname: Test name including parent suites.
doc: Test documentation.
tags: Test tags as a list of strings.
critical: yes or no depending is test considered critical or not.
template: The name of the template used for the test. An empty string if the test not templated.
starttime: Test execution execution start time.
name is the full keyword name containing possible library or resource name as a prefix. For example,
MyLibrary.Example Keyword.
type: String Keyword for normal keywords, Setup or Teardown for the top level keyword used as
setup/teardown, For for for loops, and For Item for individual for loop iterations. NOTE: Keyword
type reporting was changed in RF 3.0. See issue #2248 for details.
kwname: Name of the keyword without library or resource prefix. New in RF 2.9.
libname: Name of the library or resource the keyword belongs to, or an empty string when the
keyword is in a test case file. New in RF 2.9.
doc: Keyword documentation.
args: Keyword's arguments as a list of strings.
assign: A list of variable names that keyword's return value is assigned to. New in RF 2.9.
tags: Keyword tags as a list of strings. New in RF 3.0.
starttime: Keyword execution start time.
name is the full keyword name containing possible library or resource name as a prefix. For example,
MyLibrary.Example Keyword.
Starting from RF 3.0, this method is not called if the message has level below the current threshold level.
message message Called when the framework itself writes a syslog message.
name is the name of the imported library. If the library has been imported using the WITH NAME syntax,
name is the specified alias.
name is the name of the imported resource file without the file extension.
name is the name of the imported variable file with the file extension.
With library listeners called when the library goes out of scope.
The available methods and their arguments are also shown in a formal Java interface specification below. Contents of the java.util.Map attributes are as in
the table above. It should be remembered that a listener does not need to implement any explicit interface or have all these methods.
Listener version 3
Listener version 3 has mostly the same methods as listener version 2 but arguments of the methods related to test execution are different. This API gets actual
running and result model objects used by Robot Framework itself, and listeners can both directly query information they need and also change the model
objects on the fly.
Listener version 3 was introduced in Robot Framework 3.0. At least initially it does not have all methods that the version 2 has. The main reason is that suitable
model objects are not available internally. The close method and methods related to output files are called exactly same way in both versions.
Methods in the listener API 3
Method Arguments Documentation
start_suite data, result Called when a test suite starts.
data and result are model objects representing the executed test suite and its execution results, respectively.
end_suite data, result Called when a test suite ends.
data and result are model objects representing the executed test case and its execution results, respectively.
end_test data, result Called when a test case ends.
This method is not called if the message has level below the current threshold level.
message message Called when the framework itself writes a syslog message.
With library listeners called when the library goes out of scope.
Note
To avoid recursion, messages logged by listeners are not sent to listener methods log_message and message.
Getting information
The first example is implemented as Python module and uses the listener version 2.
ROBOT_LISTENER_API_VERSION = 2
If the above example would be saved to, for example, PauseExecution.py file, it could be used from the command line like this:
The same example could also be implemented also using the newer listener version 3 and used exactly the same way from the command line.
ROBOT_LISTENER_API_VERSION = 3
The next example, which still uses Python, is slightly more complicated. It writes all the information it gets into a text file in a temporary directory without
much formatting. The filename may be given from the command line, but also has a default value. Note that in real usage, the debug file functionality available
through the command line option --debugfile is probably more useful than this example.
import os.path
import tempfile
class PythonListener:
ROBOT_LISTENER_API_VERSION = 2
def close(self):
self.outfile.close()
The following example implements the same functionality as the previous one, but uses Java instead of Python.
import java.io.*;
import java.util.Map;
import java.util.List;
These examples illustrate how to modify the executed tests and suites as well as the execution results. All these examples require using the listener version 3.
Changing what is executed requires modifying the model object containing the executed test suite or test case objects passed as the first argument to
start_suite and start_test methods. This is illustrated by the example below that adds a new test to each executed test suite and a new keyword to each
test.
ROBOT_LISTENER_API_VERSION = 3
Trying to modify execution in end_suite or end_test methods does not work, simply because that suite or test has already been executed. Trying to modify
the name, documentation or other similar metadata of the current suite or test in start_suite or start_test method does not work either, because the
corresponding result object has already been created. Only changes to child tests or keywords actually have an effect.
This API is very similar to the pre-run modifier API that can be used to modify suites and tests before the whole test execution starts. The main benefit of using
the listener API is that modifications can be done dynamically based on execution results or otherwise. This allows, for example, interesting possibilities for
model based testing.
Although the listener interface is not built on top of Robot Framework's internal visitor interface similarly as the pre-run modifier API, listeners can still use the
visitors interface themselves. For example, the SelectEveryXthTest visitor used in pre-run modifier examples could be used like this:
ROBOT_LISTENER_API_VERSION = 3
Modifying results
Test execution results can be altered by modifying test suite and test case result objects passed as the second argument to start_suite and start_test
methods, respectively, and by modifying the message object passed to the log_message method. This is demonstrated by the following listener that is
implemented as a class.
class ResultModifier(object):
ROBOT_LISTENER_API_VERSION = 3
A limitation is that modifying the name of the current test suite or test case is not possible because it has already been written to the output.xml file when
listeners are called. Due to the same reason modifying already finished tests in the end_suite method has no effect either.
This API is very similar to the pre-Rebot modifier API that can be used to modify results before report and log are generated. The main difference is that
listeners modify also the created output.xml file.
Registering listener
A test library can register a listener by using ROBOT_LIBRARY_LISTENER attribute. The value of this attribute should be an instance of the listener to use. It may
be a totally independent listener or the library itself can act as a listener. To avoid listener methods to be exposed as keywords in the latter case, it is possible to
prefix them with an underscore. For example, instead of using end_suite or endSuite, it is possible to use _end_suite or _endSuite.
Following examples illustrates using an external listener as well as library acting as a listener itself:
import my.project.Listener;
class PythonLibraryAsListenerItself(object):
ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
ROBOT_LISTENER_API_VERSION = 2
def __init__(self):
self.ROBOT_LIBRARY_LISTENER = self
As the seconds example above already demonstrated, library listeners have to specify listener interface versions using ROBOT_LISTENER_API_VERSION attribute
exactly like any other listener.
Starting from version 2.9, you can also provide any list like object of instances in the ROBOT_LIBRARY_LISTENER attribute. This will cause all instances of the
list to be registered as listeners.
Library's listener will get notifications about all events in suites where the library is imported. In practice this means that start_suite, end_suite,
start_test, end_test, start_keyword, end_keyword, log_message, and message methods are called inside those suites.
If the library creates a new listener instance every time when the library itself is instantiated, the actual listener instance to use will change according to the test
library scope. In addition to the previously listed listener methods, close method is called when the library goes out of the scope.
See Listener interface methods section above for more information about all these methods.
To add compiled java classes to the jar, you must have a directory structure corresponding to the Java package structure and add that recursively to the zip.
For example, to add class MyLib.class, in package org.test, the file must be in org/test/MyLib.class and you can execute:
5 Supporting Tools
5.1 Library documentation tool (Libdoc)
5.2 Test data documentation tool (Testdoc)
5.3 Test data clean-up tool (Tidy)
5.4 External tools
Libdoc is Robot Framework's built-in tool for generating keyword documentation for test libraries and resource files in HTML and XML formats. The former
format is suitable for humans and the latter for RIDE and other tools. Libdoc also has few special commands to show library or resource information on the
console.
test libraries implemented with Python or Java using the normal static library API,
test libraries using the dynamic API, including remote libraries, and
resource files.
Synopsis
Options
Alternative execution
Although Libdoc is used only with Python in the synopsis above, it works also with Jython and IronPython. When documenting Java libraries, Jython is
actually required.
In the synopsis Libdoc is executed as an installed module (python -m robot.libdoc). In addition to that, it can be run also as a script:
Executing as a script can be useful if you have done manual installation or otherwise just have the robot directory with the source code somewhere in your
system.
When documenting libraries implemented with Python or that use the dynamic library API, it is possible to specify the library either by using just the library
name or path to the library source code. In the former case the library is searched using the module search path and its name must be in the same format as in
Robot Framework test data.
If these libraries require arguments when they are imported, the arguments must be catenated with the library name or path using two colons like
MyLibrary::arg1::arg2. If arguments change what keywords the library provides or otherwise alter its documentation, it might be a good idea to use --name
option to also change the library name accordingly.
A Java test library implemented using the static library API can be specified by giving the path to the source code file containing the library implementation.
When using Java 9 or newer, documentation can be generated without external dependencies, but with older Java versions the tools.jar, which is part of the
Java JDK distribution, must be found from the CLASSPATH when Libdoc is executed. Notice that generating documentation for Java libraries works only with
Jython.
Note
Generating documentation without tools.jar when using Java 9 or newer is a new feature in Robot Framework 3.1.
Resource files must always be specified using a path. If the path does not exist, resource files are also searched from all directories in the module search path
similarly as when executing test cases.
Generating documentation
When generating documentation in HTML or XML format, the output file must be specified as the second argument after the library/resource name or path.
Output format is got automatically from the extension but can also be set using the --format option.
Examples:
Libdoc has three special commands to show information on the console. These commands are used instead of the name of the output file, and they can also take
additional arguments.
list
List names of the keywords the library/resource contains. Can be limited to show only certain keywords by passing optional patterns as arguments.
Keyword is listed if its name contains given pattern.
show
Show library/resource documentation. Can be limited to show only certain keywords by passing names as arguments. Keyword is shown if its name
matches any given name. Special argument intro will show only the library introduction and importing sections.
version
Show library version
Optional patterns given to list and show are case and space insensitive. Both also accept * and ? as wildcards.
Examples:
python -m robot.libdoc Dialogs list
python -m robot.libdoc SeleniumLibrary list browser
python -m robot.libdoc Remote::10.0.0.42:8270 show
python -m robot.libdoc Dialogs show PauseExecution execute*
python -m robot.libdoc SeleniumLibrary show intro
python -m robot.libdoc SeleniumLibrary version
Python libraries
The documentation for Python libraries that use the static library API is written simply as doc strings for the library class or module and for methods
implementing keywords. The first line of the method documentation is considered as a short documentation for the keyword (used, for example, as a tool tip in
links in the generated HTML documentation), and it should thus be as describing as possible, but not too long.
The simple example below illustrates how to write the documentation in general, and there is a bit longer example at the end of this chapter containing also an
example of the generated documentation.
class ExampleLib:
"""Library for demo purposes.
def my_keyword(self):
"""Does nothing."""
pass
Examples:
| Your Keyword | xxx |
| Your Keyword | yyy |
"""
pass
Tip
If you want to use non-ASCII charactes in the documentation of Python libraries, you must either use UTF-8 as your source code encoding or create docstrings as
Unicode.
Java libraries
Documentation for Java libraries that use the static library API is written as normal Javadoc comments for the library class and methods. In this case Libdoc
actually uses the Javadoc tool internally, and thus tools.jar containing it must be in CLASSPATH. This jar file is part of the normal Java SDK distribution and
ought to be found from bin directory under the Java SDK installation.
The following simple example has exactly same documentation (and functionality) than the earlier Python example.
/**
* Library for demo purposes.
*
* This library is only used in an example and it doesn't do anything useful.
*/
public class ExampleLib {
/**
* Does nothing.
*/
public void myKeyword() {
}
/**
* Takes one argument and *does nothing* with it.
*
* Examples:
* | Your Keyword | xxx |
* | Your Keyword | yyy |
*/
public void yourKeyword(String arg) {
}
}
Dynamic libraries
To be able to generate meaningful documentation for dynamic libraries, the libraries must return keyword argument names and documentation using
get_keyword_arguments and get_keyword_documentation methods (or using their camelCase variants getKeywordArguments and
getKeywordDocumentation). Libraries can also support general library documentation via special __intro__ and __init__ values to the
get_keyword_documentation method.
See the Dynamic library API section for more information about how to create these methods.
Importing section
A separate section about how the library is imported is created based on its initialization methods. For a Python library, if it has an __init__ method that takes
arguments in addition to self, its documentation and arguments are shown. For a Java library, if it has a public constructor that accepts arguments, all its public
constructors are shown.
class TestLibrary:
What is done depends on the `mode` specified when `importing` the library.
"""
if self.mode == 'secret':
# ...
Keywords in resource files can have documentation using [Documentation] setting, and this documentation is also used by Libdoc. First line of the
documentation (until the first implicit newline or explicit \n) is considered to be the short documentation similarly as with test libraries.
Also the resource file itself can have Documentation in the Setting table for documenting the whole resource file.
Your Keyword
[Arguments] ${arg}
[Documentation] Takes one argument and *does nothing* with it.
...
... Examples:
... | Your Keyword | xxx |
... | Your Keyword | yyy |
No Operation
Robot Framework's own documentation format is the default and generally recommended format. Other formats are especially useful when using existing code
with existing documentation in test libraries.
Most important features in Robot Framework's documentation syntax are formatting using *bold* and _italic_, custom links and automatic conversion of
URLs to links, and the possibility to create tables and pre-formatted text blocks (useful for examples) simply with pipe character. If documentation gets longer,
support for section titles can also be handy.
Some of the most important formatting features are illustrated in the example below. Notice that since this is the default format, there is no need to use
ROBOT_LIBRARY_DOC_FORMAT attribute nor give the format from the command line.
def my_keyword():
"""Nothing more to see here."""
When using HTML format, you can create documentation pretty much freely using any syntax. The main drawback is that HTML markup is not that human
friendly, and that can make the documentation in the source code hard to maintain and read. Documentation in HTML format is used by Libdoc directly without
any transformation or escaping. The special syntax for linking to keywords using syntax like `My Keyword` is supported, however.
Example below contains the same formatting examples as the previous example. Now ROBOT_LIBRARY_DOC_FORMAT attribute must be used or format given on
the command line like --docformat HTML.
<ul>
<li>Formatting with <b>bold</b> and <i>italic</i>.
<li>URLs are not turned to links automatically.
<li>Custom links like <a href="http://www.w3.org/html">HTML</a> are supported.
<li>Linking to `My Keyword` works.
</ul>
"""
ROBOT_LIBRARY_DOC_FORMAT = 'HTML'
def my_keyword():
"""Nothing more to see here."""
When the plain text format is used, Libdoc uses the documentation as-is. Newlines and other whitespace are preserved except for indentation, and HTML
special characters (<>&) escaped. The only formatting done is turning URLs into clickable links and supporting internal linking like `My Keyword`.
def my_keyword():
"""Nothing more to see here."""
reStructuredText is simple yet powerful markup syntax used widely in Python projects (including this User Guide) and elsewhere. The main limitation is that
you need to have the docutils module installed to be able to generate documentation using it. Because backtick characters have special meaning in
reStructuredText, linking to keywords requires them to be escaped like \`My Keyword\`.
One of the nice features that reStructured supports is the ability to mark code blocks that can be syntax highlighted. The code block syntax has always worked
with Robot Framework, but they are highlighted only in RF 3.0.1 and newer. Syntax highlight requires additional Pygments module and supports all the
languages that Pygments supports.
__ http://docutils.sourceforge.net
.. code:: robotframework
def my_keyword():
"""Nothing more to see here."""
5.1.4 Internal linking
Libdoc supports internal linking to keywords and different sections in the documentation. Linking is done by surrounding the target name with backtick
characters like `target`. Target names are case-insensitive and possible targets are explained in the subsequent sections.
There is no error or warning if a link target is not found, but instead Libdoc just formats the text in italics. Earlier this formatting was recommended to be used
when referring to keyword arguments, but that was problematic because it could accidentally create internal links. Nowadays it is recommended to use inline
code style with double backticks like ``argument`` instead. The old formatting of single backticks may even be removed in the future in favor of giving an
error when a link target is not found.
In addition to the examples in the following sections, internal linking and argument formatting is shown also in the longer example at the end of this chapter.
Linking to keywords
All keywords the library have automatically create link targets and they can be linked using syntax `Keyword Name`. This is illustrated with the example below
where both keywords have links to each others.
def keyword(log_level="INFO"):
"""Does something and logs the output using the given level.
Valid values for log level` are "INFO" (default) "DEBUG" and "TRACE".
Note
When using reStructuredText documentation syntax, backticks must be escaped like \`Keyword Name\`.
The documentation generated by Libdoc always contains sections for overall library introduction, shortcuts to keywords, and for actual keywords. If a library
itself takes arguments, there is also separate importing section.
All these sections act as targets that can be linked, and the possible target names are listed in the table below. Using these targets is shown in the example of the
next section.
Robot Framework's documentation syntax supports custom section titles, and the titles used in the library or resource file introduction automatically create link
targets. The example below illustrates linking both to automatic and custom sections:
= My section =
def keyword():
"""Does nothing.
See `introduction` for more information and `My section` to test how
linking to custom sections works.
"""
pass
Note
Linking to custom sections works only when using Robot Framework documentation syntax.
Regardless how keywords are actually implemented, Libdoc shows arguments similarly as when creating keywords in Python. This formatting is explained
more thoroughly in the table below.
When referring to arguments in keyword documentation, it is recommended to use inline code style like ``argument``.
class LoggingLibrary:
"""Library for logging messages.
= Table of contents =
- `Usage`
- `Valid log levels`
- `Examples`
- `Importing`
- `Shortcuts`
- `Keywords`
= Usage =
This library has several keyword, for example `Log Message`, for logging
messages. In reality the library is used only for _Libdoc_ demonstration
purposes.
Valid log levels are ``INFO``, ``DEBUG``, and ``TRACE``. The default log
level can be set during `importing`.
= Examples =
See `Valid log levels` section for information about available log
levels.
Examples:
The message to log and the log level to use are defined using
``message`` and ``level`` arguments, respectively.
All standard libraries have documentation generated by Libdoc and their documentation (and source code) act as a more realistic examples.
Testdoc is Robot Framework's built-in tool for generating high level documentation based on test cases. The created documentation is in HTML format and it
includes name, documentation and other metadata of each test suite and test case, as well as the top-level keywords and their arguments.
Synopsis
Options
All options except --title have exactly the same semantics as same options have when executing test cases.
Testdoc works with all interpreters supported by Robot Framework (Python, Jython and IronPython). It can be executed as an installed module like python -m
robot.testdoc or as a script like python path/robot/testdoc.py.
Examples:
Tidy is Robot Framework's built-in a tool for cleaning up and changing the format of Robot Framework test data files.
The output is written into the standard output stream by default, but an optional output file can be given as well. Files can also be modified in-place using
--inplace or --recursive options.
Synopsis
Options
-i, Tidy given file(s) so that original file(s) are overwritten (or removed, if the format is changed). When this option is used, it is
--inplace possible to give multiple input files. Examples:
Alternative execution
Although Tidy is used only with Python in the synopsis above, it works also with Jython and IronPython. In the synopsis Tidy is executed as an installed
module (python -m robot.tidy), but it can be run also as a script:
Executing as a script can be useful if you have done manual installation or otherwise just have the robot directory with the source code somewhere in your
system.
Output encoding
All output files are written using UTF-8 encoding. Outputs written to the console use the current console encoding.
Examples:
Input format is always determined based on the extension of the input file. If output file is given, the output format is got from its extension. When using
--inplace or --recursive, it is possible to specify the desired format using the --format option.
Examples:
These tools are developed as separate projects independently from Robot Framework itself. For a list of the available tools see
http://robotframework.org/#tools.
6 Appendices
6.1 All available settings in test data
6.2 All command line options
6.3 Documentation formatting
6.4 Time format
6.5 Boolean arguments
6.6 Internal API
Note
All setting names can optionally include a colon at the end, for example Documentation:. This can make reading the settings easier especially when using the plain text
format.
Exactly same settings are available when creating tasks in the Task table.
When documenting test suites, test cases and keywords or adding metadata to test suites, newlines can be added manually using \n escape sequence.
Note
As explained in the Paragraphs section below, the single newline in Second paragraph, this time\nwith multiple lines. does not actually affect how that
paragraph is rendered. Newlines are needed when creating lists or other such constructs, though.
Adding newlines manually to a long documentation takes some effort and extra characters also make the documentation harder to read. This can be avoided,
though, as newlines are inserted automatically between continued documentation and metadata lines. In practice this means that the above example could be
written also as follows.
No automatic newline is added if a line already ends with a literal newline or if it ends with an escaping backslash. If documentation or metadata is defined in
multiple columns, cells in a same row are concatenated together with spaces. This kind of splitting can be a good idea especially when using the HTML format
and columns are narrow. Different ways to split documentation are illustrated in the examples below where all test cases end up having the same two line
documentation.
Note
Handling documentation split to multiple columns will change in Robot Framework 3.2 when also they will be concatenated together with newlines. This change should
not cause problems because, as explained in the Paragraphs section below, single newlines do not affect how paragraphs actually are rendered in logs and reports.
Example 2
[Documentation] First line
... Second line in multiple parts
No Operation
Example 3
[Documentation] First line\n
... Second line in\
... multiple parts
No Operation
With library documentations normal newlines are enough, and for example the following keyword documentation would create same end result as the test suite
documentation in the previous section.
def example_keyword():
"""First line.
6.3.2 Paragraphs
All regular text in the formatted HTML documentation is represented as paragraphs. In practice, lines separated by a single newline will be combined in a
paragraph regardless whether the newline is added manually or automatically. Multiple paragraphs can be separated with an empty line (i.e. two newlines) and
also tables, lists, and other specially formatted blocks discussed in subsequent sections end a paragraph.
The code style is created using double backticks like ``code``. The result is monospaced text with light gray background.
Asterisks, underscores or double backticks alone, or in the middle of a word, do not start formatting, but punctuation characters before or after them are
allowed. When multiple lines form a paragraph, all inline styles can span over multiple lines.
6.3.4 URLs
All strings that look like URLs are automatically converted into clickable links. Additionally, URLs that end with extension .jpg, .jpeg, .png, .gif or .bmp (case-
insensitive) will automatically create images. For example, URLs like http://example.com are turned into links, and http:///host/image.jpg and
file:///path/chart.png into images.
The automatic conversion of URLs to links is applied to all the data in logs and reports, but creating images is done only for test suite, test case and keyword
documentation, and for test suite metadata.
If neither link nor content is an image, the end result is a normal link where link is the link target and content the visible text:
If content is an image, you get a link where the link content is an image. Link target is created by link and it can be either text or image:
If link is an image but content is not, the syntax creates an image where the content is the title text shown when mouse is over the image:
= First section =
== Subsection ==
Some text.
== Second subsection ==
More text.
= Second section =
Notice that only three title levels are supported and that spaces between equal signs and the title text are mandatory.
6.3.7 Tables
Tables are created using pipe characters with spaces around them as column separators and newlines as row separators. Header cells can be created by
surrounding the cell content with equal signs and optional spaces like = Header = or =Header=. Tables cells can also contain links and formatting such as bold
and italic:
| =A= | =B= | = C = |
| _1_ | Hello | world! |
| _2_ | Hi |
The created table always has a thin border and normal text is left-aligned. Text in header cells is bold and centered. Empty cells are automatically added to
make rows equally long. For example, the above example would be formatted like this in HTML:
A B C
1 Hello world
2 Hi
6.3.8 Lists
Lists are created by starting a line with a hyphen and space ('- '). List items can be split into multiple lines by indenting continuing lines with one or more
spaces. A line that does not start with '- ' and is not indented ends the list:
Example:
- a list item
- second list item
is continued
In the following documentation, the two middle lines form a preformatted block when converted to HTML:
After block.
When documenting suites, tests or keywords in Robot Framework test data, having multiple spaces requires escaping with a backslash to prevent ignoring
spaces. The example above would thus be written like this:
---
More text...
More text...
The basic idea of this format is having first a number and then a text specifying what time that number represents. Numbers can be either integers or floating
point numbers, the whole format is case and space insensitive, and it is possible to add - prefix to specify negative times. The available time specifiers are:
days, day, d
hours, hour, h
minutes, minute, mins, min, m
seconds, second, secs, sec, s
milliseconds, millisecond, millis, ms
Examples:
1 min 30 secs
1.5 minutes
90 s
1 day 2 hours 3 minutes 4 seconds 5 milliseconds
1d 2h 3m 4s 5ms
- 10 seconds
False examples
Should Be Equal ${x} ${y} Custom error values=False # String `false` is false.
Should Be Equal ${x} ${y} Custom error values=no # Also string `no` is false.
Should Be Equal ${x} ${y} Custom error values=${EMPTY} # Empty string is false.
Should Be Equal ${x} ${y} Custom error values=${FALSE} # Python `False` is false.
Should Be Equal ${x} ${y} Custom error values=no values # Special false string with this keyword.
Note
Considering string NONE false is new in Robot Framework 3.0.3 and considering also OFF and 0 false is new in Robot Framework 3.1.