Stack Exchange Network

Stack Exchange network consists of 183 Q&A communities including Stack Overflow , the largest, most trusted online community for developers to learn, share their knowledge, and build their careers.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

setting environment variables from command line inside unit files

I am trying to set a date inside the unit file for logging

my unit file look like this:

Setting the date does not seam to be working. The error I get is the following:

Any idea how to configure it for it to work?

  • environment-variables

zozo6015's user avatar

  • 1 See also unix.stackexchange.com/questions/323914/… –  rogerdpack Commented May 23, 2019 at 16:06

Commands in ExecStart= in systemd service units do not really run on a shell, so shell expansions (such as the command substitution $(...) you use there) are not really available.

You can use them by calling a shell script explicitly, with /bin/sh -c '...' , in your ExecStartPre= . For example:

Note that you need to escape the $ itself, by using $$ , otherwise systemd will try to interpret as a systemd variable expansion. (Actually, as the next character is ( , a single $ might work there, but doubling it is the more correct setup.)

Please note that using systemctl set-environment like you're doing is really not recommended, since you're creating a global environment variable ${date} that will be available everywhere.

Instead, consider running your ExecStart= command through a shell, in which case you can define a shell variable ${date} and just use it in the single place you need it:

Note again, escaping the $ with $${date} , so systemd doesn't think it's a systemd variable to expand. Also, using exec to ensure the shell is replaced with the java process, making sure systemd will know what the main PID of the service is.

Escaping on systemd ExecStart= can become complex and burdensome quite quickly... So consider instead storing the shell script in a file (in which case you don't need to worry about escaping $ and % and about how the quotes might work slightly differently) and just run that script from the ExecStart= , that's a lot simpler (even though it requires an extra file...)

filbranden's user avatar

You must log in to answer this question.

Not the answer you're looking for browse other questions tagged environment-variables systemd systemctl ..

  • The Overflow Blog
  • One of the best ways to get value for AI coding tools: generating tests
  • The world’s largest open-source business has plans for enhancing LLMs
  • Featured on Meta
  • User activation: Learnings and opportunities
  • Site maintenance - Mon, Sept 16 2024, 21:00 UTC to Tue, Sept 17 2024, 2:00...

Hot Network Questions

  • Can All Truths Be Scientifically Verified?
  • Little spikes on mains AC
  • Doesn't nonlocality follow from nonrealism in the EPR thought experiment and Bell tests?
  • Browse a web page through SSH? (Need to access router web interface remotely, but only have SSH access to a different device on LAN)
  • Do black holes convert 100% of their mass into energy via hawking radiation?
  • Definition of annuity
  • How do I completly remove "removed" disks from mdadm array
  • How in the world do I fix this anciently old slop sink? I am okay even with a temporary fix
  • Why does counterattacking lead to a more drawish and less dynamic position than defending?
  • security concerns of executing mariadb-dump with password over ssh
  • Python script to renumber slide ids inside a pptx presentation
  • How can Sanhedrin abolish "whole body of Halachah" from the Torah?
  • Is it defamatory to publish nonsense under somebody else's name?
  • What factors cause differences between dried herb/spice brands?
  • How to NDSolve stiff ODE?
  • Tensor product of intersections in an abelian rigid monoidal category
  • Why is resonance such a widespread phenomenon?
  • Multi-producer, multi-consumer blocking queue
  • How to use a ligature that is in the dlig without using all of them
  • Why is the \[ThickSpace] character defined as 5/18 em instead of 1/4 em as in Unicode?
  • What would a planet need for rain drops to trigger explosions upon making contact with the ground?
  • History of the migration of ERA from AMS to AIMS in 2007
  • Is a thing just a class with only one member?
  • Will "universal" SMPS work at any voltage in the range, even DC?

ignoring invalid environment assignment systemd

  • Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
  • Advertising & Talent Reach devs & technologists worldwide about your product, service or employer brand
  • OverflowAI GenAI features for Teams
  • OverflowAPI Train & fine-tune LLMs
  • Labs The future of collective knowledge sharing
  • About the company Visit the blog

Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Get early access and see previews of new features.

systemd script: Assignment outside of section; Missing '='

I have the following (anonymised) systemd script called tomcat-autostart.service, stored in "/usr/lib/systemd/system/tomcat-autostart.service"

After running

I get the error message:

And I also get the following output in /var/log/messages

I had it working for a while, then I added some comments above the '[Unit]' line, and it stopped working. I then removed the comments, and probably unintentionally modified something else in the script, and I haven't been able to get it working again after that.

It seems to delight it in it's own contradiction. The first error message says that there is an assignment on the first line ('[Unit]'), and the third says that the assignment is missing an '=' sign. So basically, '[Unit]' is an assignment, except that it isn't because it is missing an '=' sign.

Searching for this problem has determined that the second error message is a direct cause of the others. Because it is ignoring the section headers, it never reads the ExecStart and ExecStop declarations. If I can solve the first and third error messages, I should be able to

What is the actual problem with my script? Running 'sudo /usr/bin/tomcat-autostart start' succeeds as expected, and the most common cause on the internet for this error message is having the script not use absolute paths (which I do in this case)

Faegy's user avatar

3 Answers 3

I have solved this issue. It turns out that the encoding of the file was changed when I added the comments (turning the file from a utf-8 encoding to a utf-16 encoding). I presume that systemd doesn't support utf-16, and was interpreting it as ASCII (or something else). Changing the encoding back to UTF-8 allowed the autostart scripts to start working again.

  • 1 Just as an aside, I got around this by installing dos2unix with apt-get install dos2unix and then running dos2unix on the file in question. –  SpeedOfSpin Commented May 15, 2018 at 8:37

Welcome to StackOverflow.

Your systemd script looks fine. Also when I copy/paste it to my own system and run it through systemd-analyze verify , it is does not generate this error-- I only get errors about file paths that don't exist on my system but should exist on yours:

I think the error was lost in translation when you anonymized it. Run systemd-analyze verify /usr/lib/systemd/system/tomcat-autostart.service and confirm the errors you are getting from your actual file.

Also, the files you modify should be in /etc/systemd/system . The files in /usr/lib are intended only be managed by packages you install.

If your file in /etc/systemd/system has the name as a parallel file in /usr/lib/systemd/system directory, it will override it.

Mark Stosberg's user avatar

  • I have double-checked that the anonymization didn't change anything. The lines that were changed were the Description, Requires, After, ExecStart, and ExecStop lines, as well as the filename of the *.service file. There were no single quotes anywhere in the file. Moving the file to /etc/systemd/system didn't change anything except for the output in /var/log/messages (which now referenced /etc/systemd/system/tomcat-autostart.service , instead of /usr/lib/systemd/system/tomcat-autostart.service ) However, I will use that directory in the future. –  Digit Cruncher Commented Feb 17, 2017 at 0:12
  • See updated answer mentioning systemd-analyze verify tool. –  Mark Stosberg Commented Feb 17, 2017 at 1:42

In my case the file had a Byte Order Mark - BOM (hexadecimal bytes EF BB BF in beginning of file to identify an UTF-8 file).

You can verify this using cat -A tomcat-autostart.service (print A ll characters, including special/invisible ones) or using hexdump -C tomcat-autostart.service (print in C anonical hex+ASCII display).

Remove these characters with your favorite text editor (like sed ) or an converter like dos2unix as Digit Cruncher suggested.

EdemarSantos's user avatar

Your Answer

Reminder: Answers generated by artificial intelligence tools are not allowed on Stack Overflow. Learn more

Sign up or log in

Post as a guest.

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .

Not the answer you're looking for? Browse other questions tagged systemd autostart or ask your own question .

  • The Overflow Blog
  • One of the best ways to get value for AI coding tools: generating tests
  • The world’s largest open-source business has plans for enhancing LLMs
  • Featured on Meta
  • User activation: Learnings and opportunities
  • Site maintenance - Mon, Sept 16 2024, 21:00 UTC to Tue, Sept 17 2024, 2:00...
  • What does a new user need in a homepage experience on Stack Overflow?
  • Announcing the new Staging Ground Reviewer Stats Widget

Hot Network Questions

  • Can All Truths Be Scientifically Verified?
  • Would a scientific theory of everything be falsifiable?
  • Will "universal" SMPS work at any voltage in the range, even DC?
  • How in the world do I fix this anciently old slop sink? I am okay even with a temporary fix
  • Need help for translating old signs
  • How frequently is random number generated when plotting function containing RandomReal?
  • Why was Panama Railroad in poor condition when US decided to build Panama Canal in 1904?
  • How to increase distance by a unit after every instance in Geometric Nodes array?
  • AWK search for multiple patterns in a file
  • Existence of 2-fold branched covers
  • Do black holes convert 100% of their mass into energy via hawking radiation?
  • Rocky Mountains Elevation Cutout
  • Ubuntu 22.04.5 - Final Point Release
  • What's the strongest material known to humanity that we could use to make Powered Armor Plates?
  • Is it a correct rendering of Acts 1,24 when the New World Translation puts in „Jehovah“ instead of Lord?
  • What is the action-cost of grabbing spell components?
  • The consequence of a good letter of recommendation when things do not work out
  • How to NDSolve stiff ODE?
  • When I use \llap to overlap words, the space between the overlapped words and the rest of the text is too much: how do I fix it?
  • How to deal with coauthors who just do a lot of unnecessary work and exploration to be seen as hard-working and grab authorship?
  • View undo history of Windows Explorer on Win11
  • Do carbon fiber wings need a wing spar?
  • Why is the \[ThickSpace] character defined as 5/18 em instead of 1/4 em as in Unicode?
  • Should I change advisors because mine doesn't object to publishing at MDPI?

ignoring invalid environment assignment systemd

  • Operating Systems & Software
  • Linux Kung Fu

Starting a systemd service at boot?

  • Thread starter StarKruzr
  • Start date Mar 29, 2015

StarKruzr

Ars Tribunus Angusticlavius

  • Mar 29, 2015

PBIS Open is a software package that permits Linux machines to join Active Directory domains. When I updated from Utopic to Vivid (which I did because numerous things I needed were broken on Utopic), it broke PBIS Open because of the switch from upstart to systemd. I have finally located (in Red Hat) a service unit file that works to start the services for this program, but even though you can "sudo service lwsmd start" (lwsmd being the metadaemon that runs PBIS Open) to start the authentication service, and I have put the lwsmd.service file in /etc/systemd/system, it doesn't start at boot, I still have to start it manually. How can I fix this? Here's the service unit file: Code: [Unit] Description=BeyondTrust PBIS Service Manager After=network.target [Service] Type=forking EnvironmentFile=/opt/pbis/libexec/init-base.sh ExecStart=/opt/pbis/sbin/lwsmd --start-as-daemon ExecReload=/opt/pbis/bin/lwsm refresh ExecStop=/opt/pbis/bin/lwsm shutdown # We want systemd to give lwsmd some time to finish gracefully, but still want # it to kill lwsmd after TimeoutStopSec if something went wrong during the # graceful stop. Normally, Systemd sends SIGTERM signal right after the # ExecStop, which would kill lwsmd. We are sending useless SIGCONT here to give # lwsmd time to finish. KillSignal=SIGCONT PrivateTmp=true [Install] WantedBy=multi-user.target nss-lookup.target I don't know why it's called "service.in" instead of "service" here. https://github.com/josephholsten/pbis/b ... service.in I replaced the relative paths with the absolute paths where the installation package installs things in /opt. But now when I start my system up I get an error message saying that "a system program has a problem," and asks if I want to report a bug, after which it says "sorry, you can't do that, it's a third-party package." Which it is, but this isn't terribly helpful :-/ It starts normally, still, if you do "sudo service lwsmd start" on the command line.  

More options

Here is some possibly useful stuff from syslog: Code: Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'alias aliastest=echo': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'alias _test_alias=true': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'case a in a) _test_alias; alias_case_works=$?;; esac': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'alias echo=echo_rename': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'alias alias=alias_replacement': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'elif [ "`uname -s`"=AIX]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'elif [ "`uname -s`"=HP-UX]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'elif [ "`uname -s`"=SunOS]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'elif [ "`uname -s`"=VMkernel]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'elif [ "`uname`"=FreeBSD-o "`uname`" = "Isilon OneFS" ]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'if [ $PLATFORM=HP-UX-o $PLATFORM = "SOLARIS" -o $PLATFORM = "FREEBSD" ]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'alias printf=/bin/printf': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'if [ "$1"=-n]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'if [ "`echo -n`"=-n]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'alias echo=echo_replacement': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'alias seq=seq_replacement': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'if [ $status=0 ]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'if [ "${PLATFORM}"=ESXI]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'if [ "$pid_comm"=<defunct>]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'if [ "$pid_comm"=${PROG_BIN}]; then': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment '( UNIX95=ps -e -o pid= -o args= | grep "^ *[0123456789]* *${PROG_BIN}" | awk '{ print $1 };' )': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment '( UNIX95=1; ps -e -o pid= -o args= | grep "^ *[0123456789]* *${PROG_BIN}" | awk '{ print $1 };' )': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'eval value=$$name': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment '[ $status=0 ] && [ ${PLATFORM} != "DEBIAN" ] && touch ${LOCKFILE}': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment '[ $? -ne 0 -a $? -ne 4 ] && status=0 && break': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: message repeated 2 times: [ Ignoring invalid environment assignment '[ $? -ne 0 -a $? -ne 4 ] && status=0 && break': /opt/pbis/libexec/init-base.sh] Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment '[ $status=0 ] && /bin/rm -f ${LOCKFILE}': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'grep -q ${AUTHD_BIN} /proc/${pid}/cmdline && pids=$pids $pid': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment '[ ! -z "`UNIX95=1 ps -p $pid -o pid=`" ] && kill "$1" $pid': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment 'eval "${name}_enable=YES"': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment '[ "$1"=start-a -n "${STARTHOOK}" ] && ${STARTHOOK}': /opt/pbis/libexec/init-base.sh Mar 29 17:35:23 aquarii systemd[1]: Ignoring invalid environment assignment '[ "$1"=start-a -n "${POSTSTARTHOOK}" ] && ${POSTSTARTHOOK}': /opt/pbis/libexec/init-base.sh "Ignoring invalid environment assignment" over and over makes me think that for some reason the contents of this "init-base.sh" file are very, very different in Ubuntu. Like it's not actually supposed to be an executable shellscript? I don't know.  

Ars Tribunus Militum

  • Mar 30, 2015

Ars Centurion

  • Apr 2, 2015
  • Apr 5, 2015

(It was "service enable.")  

alex_rhys-hurn

Seniorius lurkius.

  • May 1, 2015

Hi, Many thanks to you for providing the lwsmd.service systemd unit file. I experienced exacty your issues (i.e. works when invoked manually but not at boot). I fixed this by: 1: creating lwsmd.service in /lib/systemd/system (not in /etc/systemd/system) 2: making a symlink from /lib/systemd/system to /etc/systemd.system 3: enabling the service with systemctl enable lwsmd.service[/list] Now it works at boot time. All the best,  

brendan_kearney

systemctl start systemctl enable systemctl status ...  

Stack Exchange Network

Stack Exchange network consists of 183 Q&A communities including Stack Overflow , the largest, most trusted online community for developers to learn, share their knowledge, and build their careers.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

How do I use environment variables from file in systemd

I set environment variables in some file /etc/profile.d/mercure.sh like this :

I need them for running following command (for mercure hub ):

I want to create a mercure.service file for running mercure as service as explained here

At start, I got these errors :

I don't really understand how I can use environment variables in my ExecStart line, can somebody explain or show me an example?

  • environment-variables

GAD3R's user avatar

  • try using EnvironmentFile with static values (you'll have to rebuild file if key change of course), if dynamic are mandatory, use a wrapper, that will 1) compute keys 2) run mercure run --config ... –  Archemar Commented Jan 3 at 19:17
  • 1 I think this might be what you want: unix.stackexchange.com/a/675539/272848 –  Stewart Commented Jan 5 at 14:39
  • @Stewart thank you for link, i wrote an answer lower how i fix it :) –  Astro-Otter Commented Jan 5 at 15:40

2 Answers 2

Try a simpler environment file, without export . It is not required.

Try also ExecStart /bin/bash -c "...." if you want to use some variables like $MERCURE_PUBLISHER_JWT_KEY

Frédéric Loyer's user avatar

  • Ho, i just need to add variables in /etc/environment ? –  Astro-Otter Commented Jan 3 at 15:40
  • 1 @Astro-Otter: /etc/environment , just like the file given in the EnvironmentFile= directive, is not a shell script. It is a simple list of assignments of the form variable=value. No export , no process substitution, no nothing. Just simple variable=value statements. If you want to run a shell script, run it in ExecStart= . –  AlexP Commented Jan 3 at 23:11

Helped with answers of @frédéric-loyer and @AlexP, i set environment variables in /etc/environment and edit my mercure.service file :

It seems to be OK :

You must log in to answer this question.

Not the answer you're looking for browse other questions tagged systemd environment-variables ..

  • The Overflow Blog
  • One of the best ways to get value for AI coding tools: generating tests
  • The world’s largest open-source business has plans for enhancing LLMs
  • Featured on Meta
  • User activation: Learnings and opportunities
  • Site maintenance - Mon, Sept 16 2024, 21:00 UTC to Tue, Sept 17 2024, 2:00...

Hot Network Questions

  • Swapping front Shimano 105 R7000 34x50t 11sp Chainset with Shimano Deore FC-M5100 chainset; 11-speed 26x36t
  • C++ std::function-like queue
  • Is it a correct rendering of Acts 1,24 when the New World Translation puts in „Jehovah“ instead of Lord?
  • Would a scientific theory of everything be falsifiable?
  • Solaris 11 cbe: no more updates?
  • Doesn't nonlocality follow from nonrealism in the EPR thought experiment and Bell tests?
  • What makes amplifiers so expensive?
  • Why was Esther included in the canon?
  • What is the action-cost of grabbing spell components?
  • Longtable goes beyond the right margin and footnote does not fit under the table
  • Where is DC-3 parked at KOPF?
  • How frequently is random number generated when plotting function containing RandomReal?
  • How to deal with coauthors who just do a lot of unnecessary work and exploration to be seen as hard-working and grab authorship?
  • Ubuntu 22.04.5 - Final Point Release
  • Why does counterattacking lead to a more drawish and less dynamic position than defending?
  • Python script to renumber slide ids inside a pptx presentation
  • How many engineers/scientists believed that human flight was imminent as of the late 19th/early 20th century?
  • "There is a bra for every ket, but there is not a ket for every bra"
  • O(nloglogn) Sorting Algorithm?
  • Latin lyrics to "Far away"
  • Need help for translating old signs
  • Why did early ASCII have ← and ↑ but not ↓ or →?
  • Who pays the cost of Star Alliance lounge usage for cross-airline access?
  • Tensor product of intersections in an abelian rigid monoidal category

ignoring invalid environment assignment systemd

Select Your Language

[/usr/lib/systemd/system/mdcheck_xxx.service:14] invalid environment assignment, ignoring: mdadm_check_duration="6 hours" is shown in /var/log/messages..

systemd error message in /var/log/message :

mdcheck_start.service fail to start.

Environment

  • Red Hat Enterprise Linux 7.6
  • mdadm-4.1-4.el7.x86_64

Subscriber exclusive content

A Red Hat subscription provides unlimited access to our knowledgebase, tools, and much more.

Current Customers and Partners

Log in for full access

New to Red Hat?

Learn more about Red Hat subscriptions

Using a Red Hat product through a public cloud?

How to access this content

Quick Links

  • Subscriptions
  • Support Cases
  • Customer Service
  • Product Documentation
  • Contact Customer Portal
  • Customer Portal FAQ
  • Log-in Assistance
  • Trust Red Hat
  • Browser Support Policy
  • Accessibility
  • Awards and Recognition

Related Sites

  • developers.redhat.com
  • connect.redhat.com
  • cloud.redhat.com

Systems Status

  • Red Hat Subscription Value
  • About Red Hat
  • Red Hat Jobs

Red Hat legal and privacy links

  • Contact Red Hat
  • Red Hat Blog
  • Diversity, equity, and inclusion
  • Cool Stuff Store
  • Red Hat Summit
  • Privacy statement
  • Terms of use
  • All policies and guidelines
  • Digital accessibility

Formatting Tips

Here are the common uses of Markdown.

Request a English Translation

Generating machine translation.

Stack Exchange Network

Stack Exchange network consists of 183 Q&A communities including Stack Overflow , the largest, most trusted online community for developers to learn, share their knowledge, and build their careers.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Tomcat service does not see $JAVA_HOME

I'm trying to setup a Tomcat service with Systemd on CentOS 7.

I've installed Oracle Java 1.8u74 to /usr/java/jdk1.8.0_74 and set the environment variable $JAVA_HOME at boot like so:

When I login to the system I can run echo $JAVA_HOME and see the correct path. I have installed Tomcat and the tomcat.service file has the following:

The problem is that if I omit the line that reads Environment=JAVA_HOME=/usr/java/jdk1.8.0_74 , Tomcat does not find $JAVA_HOME, but I expect it to find $JAVA_HOME because it is set in /etc/profile.d/setenv.sh .

My questions

  • Is it not found due to boot sequence (i.e. setenv.sh runs after the Systemd start)?
  • Did I put setenv.sh in the wrong place?
  • What's the best way to handle this?
  • environment-variables

slm's user avatar

This is the expected behavior with systemd.

To understand what the environment that systemd services run in, you can refer to man systemd.exec , specifically the section ENVIRONMENT VARIABLES IN SPAWNED PROCESSES . As it says, only very few variables are set, and you have to set anything else yourself.

It just happens that the files in /etc/profile.d are sourced by interactive shells, which is why you can see the variable when you log in. What you have done is exactly what the tutorial recommends, so you can just stick to it. If you are worried that a Java upgrade would break your unit file, you can make a symlink to your java installation, or even source the file at startup:

I would personally stick with what you have already.

chutz's user avatar

You must log in to answer this question.

Not the answer you're looking for browse other questions tagged linux java centos7 systemd environment-variables ..

  • The Overflow Blog
  • One of the best ways to get value for AI coding tools: generating tests
  • The world’s largest open-source business has plans for enhancing LLMs
  • Featured on Meta
  • User activation: Learnings and opportunities
  • Site maintenance - Mon, Sept 16 2024, 21:00 UTC to Tue, Sept 17 2024, 2:00...

Hot Network Questions

  • Ubuntu 22.04.5 - Final Point Release
  • C++ std::function-like queue
  • What’s the name of this horror movie where a girl dies and comes back to life evil?
  • Place with signs in Chinese & Arabic
  • Understanding symmetry in a double integral
  • Copyright Fair Use: Is using the phrase "Courtesy of" legally acceptable when no permission has been given?
  • Whom did Jesus' followers accompany -- a soldier or a civilian?
  • Longtable goes beyond the right margin and footnote does not fit under the table
  • Where is DC-3 parked at KOPF?
  • How to avoid bringing paper silverfish home from a vacation place?
  • Browse a web page through SSH? (Need to access router web interface remotely, but only have SSH access to a different device on LAN)
  • How to reply to a revise and resubmit review, saying is all good?
  • History of the migration of ERA from AMS to AIMS in 2007
  • How frequently is random number generated when plotting function containing RandomReal?
  • Why is steaming food faster than boiling it?
  • Solaris 11 cbe: no more updates?
  • The meaning of an implication with the existential quantifier
  • "There is a bra for every ket, but there is not a ket for every bra"
  • Offset+Length vs 2 Offsets
  • What is the oldest open math problem outside of number theory?
  • AWK search for multiple patterns in a file
  • How can Sanhedrin abolish "whole body of Halachah" from the Torah?
  • Is a thing just a class with only one member?
  • Could Prop be the top universe?

ignoring invalid environment assignment systemd

Stack Exchange Network

Stack Exchange network consists of 183 Q&A communities including Stack Overflow , the largest, most trusted online community for developers to learn, share their knowledge, and build their careers.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

systemctl .service can't read the environment variable

I'm using django and now trying to configure server with nginx and uwsi. now i'm running nginx+socket+uwsgi_emperor well . for running uwsgi i use below command in terminal:

and it's working great. now i want add service with systemctl {start|stop|restart|status} uwsgi.service so i can easily use uwsgi. the problem here occur.here is /etc/systemd/system/uwsgi.service:

now when i start it with systemctl start uwsgi.service its running and i can see systemctl status uwsgi.service thaat is loaded well but when i want access the ip the error appear in journalctl -b -u uwsgi that shown my django can't access(read) the environment variable.because i using several environment variable in django's setting so without value of them django can't work correctly.why this happen?(when i running uwsgi in command line all is good but when i want use same command in systemctl django can't read environment variable and error occur ). thank you

  • environment-variables

mehdi's user avatar

2 Answers 2

that shown my django can't access(read) the environment variable

Because systemd is not running in a shell with your user and that user environment and those variables.

when i running uwsgi in command line

then you are using your own user with that environment and those variables.

before your ExecStart . Or if it is not that many you can also do

(one line per variable) where

  • {VAR} is the name of your environment variable
  • {VALUE} is the value of that {VAR}

Rinzwind's user avatar

One other approach in addition to the accepted answer is to have this /bin/bash -lc '<your command>' for your ExecStart or ExecStop

and have a corresponding /etc/profile file with the environment variables.

Vysakh Sreenivasan's user avatar

You must log in to answer this question.

Not the answer you're looking for browse other questions tagged 18.04 environment-variables django uwsgi ..

  • The Overflow Blog
  • One of the best ways to get value for AI coding tools: generating tests
  • The world’s largest open-source business has plans for enhancing LLMs
  • Featured on Meta
  • User activation: Learnings and opportunities
  • Site maintenance - Mon, Sept 16 2024, 21:00 UTC to Tue, Sept 17 2024, 2:00...

Hot Network Questions

  • What is the action-cost of grabbing spell components?
  • Does the science work for why my trolls explode?
  • Solaris 11 cbe: no more updates?
  • AWK search for multiple patterns in a file
  • 1950s comic book about bowling ball looking creatures that inhabit the underground of Earth
  • How frequently is random number generated when plotting function containing RandomReal?
  • In Photoshop, when saving as PNG, why is the size of my output file bigger when I have more invisible layers in the original file?
  • How to reply to a revise and resubmit review, saying is all good?
  • Little spikes on mains AC
  • What factors cause differences between dried herb/spice brands?
  • How can I switch from MAG to TRU heading in a 737?
  • How to increase distance by a unit after every instance in Geometric Nodes array?
  • "There is a bra for every ket, but there is not a ket for every bra"
  • Need help for translating old signs
  • Should I change advisors because mine doesn't object to publishing at MDPI?
  • How many engineers/scientists believed that human flight was imminent as of the late 19th/early 20th century?
  • Browse a web page through SSH? (Need to access router web interface remotely, but only have SSH access to a different device on LAN)
  • Should I be careful about setting levels too high at one point in a track?
  • What would a planet need for rain drops to trigger explosions upon making contact with the ground?
  • Offset+Length vs 2 Offsets
  • Whom did Jesus' followers accompany -- a soldier or a civilian?
  • Would a scientific theory of everything be falsifiable?
  • Why does counterattacking lead to a more drawish and less dynamic position than defending?
  • Drill perpendicular hole through thick lumber using handheld drill

ignoring invalid environment assignment systemd

How do I use environment variables when running as service?

I am using Logstash v5.1.0 on Ubuntu 16.04 and my logstash conf file includes an environment variable reference to set the output graphite server, but I cannot get it to work when running as a service.

My config file contains (missing a few lines for clarity): graphite { host => "${GRAPHITE_SERVER}" }

and my /etc/environment file contains: GRAPHITE_SERVER=54.40.235.208

Yet with configuration file logging, I see: fetched an invalid config {} reason=>"Cannot evaluate ${GRAPHITE_SERVER} . Environment variable GRAPHITE_SERVER is not set and there is no default value given."

This does work if I run logstash from bash manually.

Any help would be much appreciated - thanks. Andrew

Hi @astickler ,

I haven't tried it on 16.04 but on 14.04 it works when exporting it in /etc/default/logstash . E.g. export GRAPHITE_SERVER=54.40.235.208 . I know, it sounds strange to prefix with export in that file, see https://github.com/elastic/logstash/issues/6414 for discussion.

14.04 uses upstart while 16.04 is using systemd, so they are probably souring the file in different ways, but it's worth a try.

Hi Jakob - I had already tried that, but it also doesn't work. Andrew

I just tried this on a 16.04 VM and adding it to /etc/default/logstash with the export prefixed resulted in:

However, just using LS_FPATH="/tmp/" worked for me.

I tested with Logstash 5.1.2, mind giving that a try and using my simple config, for testing?

Jakob - this is good news, your test worked with my currently installed 5.1.1 version, and when I tried the same with my config.

I know I tried this before and it didn't work, but I have realised I may have been confused by old errors in the logstash_plain log because my service is taking nearly 2 minutes to start (I am on an AWS instance, and although I have improved the entropy, it appears it's not yet good enough).

Anyway - all working now - thanks for your help. Andrew

Glad to hear it's working now. About the entropy issue, see this GH issue , you might want to give haveged a try. Worked good for me to resolve this.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.

:
: CLOSED ERRATA
None
Red Hat Enterprise Linux 7
Red Hat
7.7
Unspecified
Unspecified
unspecified
unspecified
rc
: ---
XiaoNi
Fine Fan
: ( )
TreeView+ /
2019-11-20 05:43 UTC by mezhang
2023-12-15 16:58 UTC ( )
22 users ( )
mdadm-4.1-5.el7
If docs needed, set a value
2020-09-29 20:16:25 UTC

Attachments
Links
System ID Private Priority Status Summary Last Updated
Red Hat Product Errata 0 None None None 2020-09-29 20:16:29 UTC
mezhang 2019-11-20 05:43:43 UTC Nigel Croxon 2019-11-21 17:09:34 UTC mezhang 2019-11-22 03:34:04 UTC I didn't install additional optional packages during installation. Besides, my test environment is a vm. Mengyi mezhang 2019-11-22 03:52:55 UTC MatthewH 2019-12-04 08:35:58 UTC ) As far as I can tell the problem is the systemd components included in the latest mdadm package are incompatible with the version of systemd included with RHEL/CentOS 7. In my case I don’t just see these errors, but also can no longer automount my RAID array due to systemd ordering cycle problems. Thanks Jonathan Earl Brassow 2019-12-16 16:41:46 UTC Nigel Croxon 2019-12-16 18:49:39 UTC XiaoNi 2019-12-17 06:25:42 UTC Michael Radda (HEROLD) 2020-01-09 10:30:07 UTC sdavis4fun 2020-02-01 09:47:36 UTC XiaoNi 2020-02-03 02:25:10 UTC Nigel Croxon 2020-04-17 16:38:34 UTC has been marked as a duplicate of this bug. *** Fine Fan 2020-04-29 01:33:23 UTC Andrew Bauer 2020-05-03 13:29:55 UTC >$ rpm -ql mdadm | grep cron >/etc/cron.d/raid-check Martin Zahn 2020-09-14 08:38:25 UTC errata-xmlrpc 2020-09-29 20:16:25 UTC Armstrong 2022-11-11 14:08:11 UTC Comment hidden (spam) '; });
You need to before you can comment on or make changes to this bug.

Navigation Menu

Search code, repositories, users, issues, pull requests..., provide feedback.

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly.

To see all available qualifiers, see our documentation .

  • Notifications You must be signed in to change notification settings

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Systemd ignores system-wide environment variables #106

@mpurzynski

mpurzynski commented Jun 29, 2018

I'm trying to define a couple of environment variables

http_proxy=" "
https_proxy=" "
no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com, 1.2.3.4/24, *.domain.name, *.domain2.name, 169.$
HTTP_PROXY=" "
HTTPS_PROXY=" "
NO_PROXY="localhost,127.0.0.1,localaddress,.localdomain.com, 1.2.3.4/24, *.domain.name, *.domain2.name, 169.$

I'd like to have them defined globally, for every user (in the user's environment) and for every starting service. Here is what I tried:

cat /etc/systemd/system-environment-generators/30-proxies.sh

#!/usr/bin/bash

http_proxy=' '
echo http_proxy=$http_proxy

This file has permissions 0755 and executes correctly from the command line

No luck. I tried user-environment-generators as well with the same result

cat /etc/environment.d/30-proxy.conf

ala=makota

(just as a test)

Also no luck.

I tried reloading systemd with systemctl daemon-reload, logged out, in, etc. Nothing helps.

I kind of feel frustrated that after 10+ years of using Linux on a daily basis I do not see a clear way to define a system-wide environment variable.

After further debugging, looks like systemd does not export variables to the current shell

systemctl --system show-environment

systemctl --user show-environment

Sorry, something went wrong.

@miguelinux

miguelinux commented Jun 29, 2018

Have you tried to place your script at directory?
or define your variables in file?

@ahkok

ahkok commented Jul 2, 2018

I'm wondering if this is either a usage issue, or an upstream problem. You may have to reboot in order for changes to take effect. I don't see any incorrect paths in your post, though.

mpurzynski commented Jul 2, 2018

We have a design problem here.

I'll try to export things in /etc/profile next

Exporting variables in /etc/profile works for ssh, su, sudo.

Doing so does NOT export anything to systemd - so services won't see those variables.

One needs to export the same set of variables for systemd, either as generators (systemd-wide but not system-wide), or per service and also for interactive environments, with /etc/profile

Can we have this systemd design bug documented?

that would be an upstream thing.

@iphutch

No branches or pull requests

@miguelinux

Robot Framework User Guide

Version 7.1.

Table of Contents

1.1.1   Why Robot Framework?

1.1.2   high-level architecture, 1.1.3   screenshots, 1.1.4   getting more information, 1.2   copyright and license, 1.3.1   python installation, 1.3.2   installing using pip, 1.3.3   installing from source, 1.3.4   verifying installation, 1.3.5   virtual environments, 1.4   demonstrations, 2.1.1   files and directories, 2.1.2   test data sections, 2.1.3   supported file formats, 2.1.4   rules for parsing the data, 2.1.5   localization, 2.1.6   style, 2.2.1   test case syntax, 2.2.2   using arguments, 2.2.3   failures, 2.2.4   test case name and documentation, 2.2.5   tagging test cases, 2.2.6   test setup and teardown, 2.2.7   test templates, 2.2.8   different test case styles, 2.3.1   task syntax, 2.3.2   task related settings, 2.4.1   suite files, 2.4.2   suite directories, 2.4.3   suite name, 2.4.4   suite documentation, 2.4.5   free suite metadata, 2.4.6   suite setup and teardown, 2.5.1   importing libraries, 2.5.2   specifying library to import, 2.5.3   setting custom name to library, 2.5.4   standard libraries, 2.5.5   external libraries, 2.6.1   introduction, 2.6.2   using variables, 2.6.3   creating variables, 2.6.4   built-in variables, 2.6.5   variable priorities and scopes, 2.6.6   advanced variable features, 2.7.1   user keyword syntax, 2.7.2   user keyword name and documentation, 2.7.3   user keyword tags, 2.7.4   user keyword arguments, 2.7.5   embedding arguments into keyword name, 2.7.6   user keyword return values, 2.7.7   user keyword setup and teardown, 2.7.8   private user keywords, 2.8.1   resource files, 2.8.2   variable files, 2.9.1    for loops, 2.9.2    while loops, 2.9.3   loop control using break and continue, 2.9.4    if/else syntax, 2.9.5    try/except syntax, 2.10.1   handling keywords with same names, 2.10.2   timeouts, 2.10.3   parallel execution of keywords, 3.1.1   starting test execution, 3.1.2   using command line options, 3.1.3   test results, 3.1.4   argument files, 3.1.5   getting help and version information, 3.1.6   creating start-up scripts, 3.1.7   making *.robot files executable, 3.1.8   debugging problems, 3.2.1   execution flow, 3.2.2   test and suite statuses, 3.2.3   continuing on failure, 3.2.4   stopping test execution gracefully, 3.3.1   generic automation mode, 3.3.2   task related command line options, 3.4.1   using rebot, 3.4.2   creating reports, logs and output files, 3.4.3   combining outputs, 3.4.4   merging outputs, 3.4.5   json output files, 3.5.1   selecting files to parse, 3.5.2   selecting test cases, 3.5.3   setting metadata, 3.5.4   configuring where to search libraries and other extensions, 3.5.5   setting variables, 3.5.6   dry run, 3.5.7   randomizing execution order, 3.5.8   programmatic modification of test data, 3.5.9   controlling console output, 3.5.10   setting listeners, 3.6.1   different output files, 3.6.2   log levels, 3.6.3   splitting logs, 3.6.4   configuring statistics, 3.6.5   removing and flattening keywords, 3.6.6   automatically expanding keywords, 3.6.7   setting start and end time of execution, 3.6.8   limiting error message length in reports, 3.6.9   programmatic modification of results, 3.6.10   system log, 4.1.1   introduction, 4.1.2   creating test library class or module, 4.1.3   creating keywords, 4.1.4   communicating with robot framework, 4.1.5   distributing test libraries, 4.1.6   dynamic library api, 4.1.7   hybrid library api, 4.1.8   using robot framework's internal modules, 4.1.9   extending existing test libraries, 4.2.1   introduction, 4.2.2   putting remote library to use, 4.2.3   supported argument and return value types, 4.2.4   remote protocol, 4.3.1   listener structure, 4.3.2   listener interface versions, 4.3.3   taking listeners into use, 4.3.4   listener calling order, 4.3.5   listener examples, 4.4.1   taking parsers into use, 4.4.2   parser api, 4.4.3   examples, 5.1.1   general usage, 5.1.2   writing documentation, 5.1.3   documentation syntax, 5.1.4   internal linking, 5.1.5   representing arguments, 5.1.6   libdoc example, 5.2.1   general usage, 5.2.2   generating documentation, 5.3   test data clean-up tool (tidy), 5.4   external tools, 6.1.1   setting section, 6.1.2   test case section, 6.1.3   keyword section, 6.2.1   command line options for test execution, 6.2.2   command line options for post-processing outputs, 6.2.3   environment variables for execution and post-processing, 6.3.1   bulgarian (bg), 6.3.2   bosnian (bs), 6.3.3   czech (cs), 6.3.4   german (de), 6.3.5   spanish (es), 6.3.6   finnish (fi), 6.3.7   french (fr), 6.3.8   hindi (hi), 6.3.9   italian (it), 6.3.10   japanese (ja), 6.3.11   korean (ko), 6.3.12   dutch (nl), 6.3.13   polish (pl), 6.3.14   portuguese (pt), 6.3.15   brazilian portuguese (pt-br), 6.3.16   romanian (ro), 6.3.17   russian (ru), 6.3.18   swedish (sv), 6.3.19   thai (th), 6.3.20   turkish (tr), 6.3.21   ukrainian (uk), 6.3.22   vietnamese (vi), 6.3.23   chinese simplified (zh-cn), 6.3.24   chinese traditional (zh-tw), 6.4.1   handling whitespace in test data, 6.4.2   paragraphs, 6.4.3   inline styles, 6.4.4   urls, 6.4.5   custom links and images, 6.4.6   section titles, 6.4.7   tables, 6.4.8   lists, 6.4.9   preformatted text, 6.4.10   horizontal ruler, 6.5.1   time as number, 6.5.2   time as time string, 6.5.3   time as "timer" string, 6.6   boolean arguments, 6.7.1   introduction, 6.7.2   evaluation namespace, 6.7.3   using variables, 6.8.1   suite file extensions, 6.8.2   resource file extensions, 6.8.3   media type, 6.8.4   remote server port, 1   getting started, 1.1   introduction, 1.3   installation instructions.

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 .

The official RPA 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 .

Project pages

Mailing lists.

  • Enables easy-to-use tabular syntax for creating test cases in a uniform way.
  • Provides ability to create reusable higher-level keywords from the existing keywords.
  • Provides easy-to-read result reports and logs in HTML format.
  • Is platform and application independent.
  • Provides a simple library API for creating customized test libraries which can be implemented natively with Python.
  • Provides a command line interface and XML based output files for integration into existing build infrastructure (continuous integration systems).
  • Provides support for testing web applications, rest APIs, mobile applications, running processes, connecting to remote systems via Telnet or SSH, and so on.
  • Supports creating data-driven test cases .
  • Has built-in support for variables , practical particularly for testing in different environments.
  • Provides tagging to categorize and select test cases to be executed.
  • Enables easy integration with source control: test suites are just files and directories that can be versioned with the production code.
  • Provides test-case and test-suite -level setup and teardown.
  • The modular architecture supports creating tests even for applications with several diverse interfaces.

Robot Framework is a generic, application and technology independent framework. It has a highly modular architecture illustrated in the diagram below.

src/GettingStarted/architecture.png

Robot Framework architecture

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.

Following screenshots show examples of the test data and created reports and logs .

src/GettingStarted/testdata_screenshots.png

Test case file

src/GettingStarted/screenshots.png

Reports and logs

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 .

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 .

Robot Framework is open source software provided under the Apache License 2.0 . Robot Framework documentation such as this User Guide use the Creative Commons Attribution 3.0 Unported license. Most libraries and tools in the larger ecosystem around the framework are also open source, but they may use different licenses.

The full Robot Framework copyright notice is included below:

These instructions cover installing Robot Framework and its preconditions on different operating systems. If you already have Python installed, you can install Robot Framework using the standard package manager pip :

Installing Python on Linux

Installing python on windows, installing python on macos, pypy installation, configuring path, running pip command, installing and uninstalling robot framework.

Robot Framework is implemented using Python , and a precondition to install it is having Python or its alternative implementation PyPy installed. Another recommended precondition is having the pip package manager available.

Robot Framework requires Python 3.8 or newer. The latest version that supports Python 3.6 and 3.7 is Robot Framework 6.1.1 . If you need to use Python 2, Jython or IronPython , you can use Robot Framework 4.1.3 .

On Linux you should have suitable Python installation with pip available by default. If not, you need to consult your distributions documentation to learn how to install them. This is also true if you want to use some other Python version than the one provided by your distribution by default.

To check what Python version you have installed, you can run python --version command in a terminal:

Notice that if your distribution provides also older Python 2, running python may use that. To use Python 3, you can use python3 command or even more version specific command like python3.8 . You need to use these version specific variants also if you have multiple Python 3 versions installed and need to pinpoint which one to use:

Installing Robot Framework directly under the system provided Python has a risk that possible problems can affect the whole Python installation used also by the operating system itself. Nowadays Linux distributions typically use user installs by default to avoid such problems, but users can also themselves decide to use virtual environments .

On Windows Python is not available by default, but it is easy to install. The recommended way to install it is using the official Windows installers available at http://python.org . For other alternatives, such as installing from the Microsoft Store, see the official Python documentation .

When installing Python on Windows, it is recommended to add Python to PATH to make it and tools like pip and Robot Framework easier to execute from the command line. When using the official installer , you just need to select the Add Python 3.x to PATH checkbox on the first dialog.

To make sure Python installation has been successful and Python has been added to PATH , you can open the command prompt and execute python --version :

If you install multiple Python versions on Windows, the version that is used when you execute python is the one first in PATH . If you need to use others, the easiest way is using the py launcher :

MacOS does not provide Python 3 compatible Python version by default, so it needs to be installed separately. The recommended approach is using the official macOS installers available at http://python.org . If you are using a package manager like Homebrew , installing Python via it is possible as well.

You can validate Python installation on macOS using python --version like on other operating systems.

PyPy is an alternative Python implementation. 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 . To validate that PyPy installation was successful, run pypy --version or pypy3 --version .

Using Robot Framework with PyPy is officially supported only on Linux.

The PATH environment variable lists directories where commands executed in a system are searched from. To make using Python, pip and Robot Framework easier from the command line, it is recommended to add the Python installation directory as well as the directory where commands like pip and robot are installed into PATH .

When using Python on Linux or macOS, Python and tools installed with it should be automatically in PATH . If you nevertheless need to update PATH , you typically need to edit some system wide or user specific configuration file. Which file to edit and how depends on the operating system and you need to consult its documentation for more details.

On Windows the easiest way to make sure PATH is configured correctly is setting the Add Python 3.x to PATH checkbox when running the installer . To manually modify PATH on Windows, follow these steps:

  • Find Environment Variables under Settings . There are variables affecting the whole system and variables affecting only the current user. Modifying the former will require admin rights, but modifying the latter is typically enough.
  • Select PATH (often written like Path ) and click Edit . If you are editing user variables and PATH does not exist, click New instead.
  • Add both the Python installation directory and the Scripts directory under the installation directory into PATH .
  • Exit the dialog with Ok to save the changes.
  • Start a new command prompt for the changes to take effect.

These instructions cover installing Robot Framework using pip , the standard Python package manager. If you are using some other package manager like Conda , you can use it instead but need to study its documentation for instructions.

When installing Python, you typically get pip installed automatically. If that is not the case, you need to check the documentation of that Python installation for instructions how to install it separately.

Typically you use pip by running the pip command, but on Linux you may need to use pip3 or even more Python version specific variant like pip3.8 instead. When running pip or any of its variants, the pip version that is found first in PATH will be used. If you have multiple Python versions installed, you may need to pinpoint which exact version you want to use. This is typically easiest done by running python -m pip and substituting python with the Python version you want to use.

To make sure you have pip available, you can run pip --version or equivalent.

Examples on Linux:

Examples on Windows:

In the subsequent sections pip is always run using the pip command. You may need to use some of the other approaches explained above in your environment.

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.

Another installation alternative is getting Robot Framework source code and installing it using the provided setup.py script. This approach is recommended only if you do not have pip available for some reason.

You can get the source code by downloading a source distribution as a zip package from PyPI and extracting it. An alternative is cloning the GitHub repository and checking out the needed release tag.

Once you have the source code, you can install it with the following command:

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.

To make sure that the correct Robot Framework version has been installed, run the following command:

If running these commands fails with a message saying that the command is not found or recognized, a good first step is double-checking the PATH configuration.

If you have installed Robot Framework under multiple Python versions, running robot will execute the one first in PATH . To select explicitly, you can run python -m robot and substitute python with the right Python version.

Python virtual environments allow Python packages to be installed in an isolated location for a particular system or application, rather than installing all packages into the same global location. They have two main use cases:

  • Install packages needed by different projects into their own environments. This avoids conflicts if projects need different versions of same packages.
  • Avoid installing everything under the global Python installation. This is especially important on Linux where the global Python installation may be used by the distribution itself and messing it up can cause severe problems.

There are several demo projects that introduce Robot Framework and help getting started with it.

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   control structures, 2.10   advanced features.

This section covers Robot Framework's overall test data syntax. The following sections will explain how to actually create test cases, test suites and so on. Although this section mostly uses term test , the same rules apply also when creating tasks .

Space separated format

Pipe separated format, restructuredtext format, json format, ignored data, dividing data to several rows, enabling languages, built-in languages, custom language files, contributing translations.

The hierarchical structure for arranging test cases is built as follows:

  • Test cases are created in suite files .
  • A test case file automatically creates a test suite containing the test cases in that file.
  • A directory containing test case files forms a higher-level test suite. Such a suite directory has suites created from test case files as its child test suites.
  • A test suite directory can also contain other test suite directories, and this hierarchical structure can be as deeply nested as needed.
  • Test suite directories can have a special initialization file configuring the created test suite.

In addition to this, there are:

  • Test libraries containing the lowest-level keywords.
  • Resource files with variables and higher-level user keywords .
  • Variable files to provide more flexible ways to create variables than resource files.

Test case files, test suite initialization files and resource files are all created using Robot Framework test data syntax. Test libraries and variable files are created using "real" programming languages, most often Python.

Robot Framework data is defined in different sections, often also called tables, listed below:

Different sections in data
Section Used for
Settings , and . and .
Variables Defining that can be used elsewhere in the test data.
Test Cases from available keywords.
Tasks using available keywords. Single file can only contain either tests or tasks.
Keywords from existing lower-level keywords
Comments Additional comments or data. Ignored by Robot Framework.

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 at least one asterisk in the beginning. For example, also *settings would be recognized as a section header.

Robot Framework supports also singular headers like *** Setting ***, but that support was deprecated in Robot Framework 6.0. There is a visible deprecation warning starting from Robot Framework 7.0 and singular headers will eventually not be supported at all.

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 .

Possible data before the first section is ignored.

Section headers can be localized . See the Translations appendix for supported translations.

The most common approach to create Robot Framework data is using the space separated format where pieces of the data, such as keywords and their arguments, are separated from each others with two or more spaces. An alternative is using the pipe separated format where the separator is the pipe character surrounded with spaces ( | ).

Suite files typically use the .robot extension, but what files are parsed can be configured . Resource files can use the .robot extension as well, but using the dedicated .resource extension is recommended and may be mandated in the future. Files containing non-ASCII characters must be saved using the UTF-8 encoding.

Robot Framework supports also reStructuredText files so that normal Robot Framework data is embedded into code blocks . Only files with the .robot.rst extension are parsed by default. If you would rather use just .rst or .rest extension, that needs to be configured separately.

Robot Framework data can also be created in the JSON format that is targeted more for tool developers than normal Robot Framework users. Only JSON files with the custom .rbt extension are parsed by default.

Earlier Robot Framework versions supported data also in HTML and TSV formats. The TSV format still works if the data is compatible with the space separated format , but the support for the HTML format has been removed altogether. If you encounter such data files, you need to convert them to the plain text format to be able to use them with Robot Framework 3.2 or newer. The easiest way to do that is using the Tidy tool, but you must use the version included with Robot Framework 3.1 because newer versions do not understand the HTML format at all.

When Robot Framework parses data, it first splits the data to lines and then lines to tokens such as keywords and arguments. When using the space separated format, the separator between tokens is two or more spaces or alternatively one or more tab characters. In addition to the normal ASCII space, any Unicode character considered to be a space (e.g. no-break space) works as a separator. 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 when it makes the data easier to understand.

Because tabs and consecutive spaces are considered separators, they must be escaped if they are needed in keyword arguments or elsewhere in the actual data. It is possible to use special escape syntax like \t for tab and \xA0 for no-break space as well as built-in variables ${SPACE} and ${EMPTY} . See the Escaping section for details.

Although using two spaces as a separator is enough, it is recommended to use four spaces to make the separator easier to recognize.

Prior to Robot Framework 3.2, non-ASCII spaces used in the data were converted to ASCII spaces during parsing. Nowadays all data is preserved as-is.

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 or tab 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.

When using the pipe separated format, consecutive spaces or tabs inside arguments do not need to be escaped. Similarly empty columns do not need to be escaped except if they are at the end . Possible pipes surrounded by spaces in the actual test data must be escaped with a backslash, though:

Preserving consecutive spaces and tabs in arguments is new in Robot Framework 3.2. Prior to it non-ASCII spaces used in the data were also converted to ASCII spaces.

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.

Using reStructuredText files with Robot Framework requires the Python docutils module to be installed.

When using Robot Framework with reStructuredText files, normal Robot Framework data is embedded to so called code blocks. In standard reST code blocks are marked using the code directive, but Robot Framework supports also code-block or sourcecode directives used by the Sphinx tool.

Robot Framework supports reStructuredText files using .robot.rst , .rst and .rest extensions. To avoid parsing unrelated reStructuredText files, only files with the .robot.rst extension are parsed by default when executing a directory. Parsing files with other extensions can be enabled by using either --parseinclude or --extension option.

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.

Parsing .robot.rst files automatically is new in Robot Framework 6.1.

Robot Framework supports data also in the JSON format. This format is designed more for tool developers than for regular Robot Framework users and it is not meant to be edited manually. Its most important use cases are:

  • Transferring data between processes and machines. A suite can be converted to JSON in one machine and recreated somewhere else.
  • Saving a suite, possibly a nested suite, constructed from normal Robot Framework data into a single JSON file that is faster to parse.
  • Alternative data format for external tools generating tests or tasks.

The JSON data support is new in Robot Framework 6.1 and it can be enhanced in future Robot Framework versions. If you have an enhancement idea or believe you have encountered a bug, please submit an issue or start a discussion thread on the #devel channel on our Slack .

Converting suite to JSON

A suite structure can be serialized into JSON by using the TestSuite.to_json method. When used without arguments, it returns JSON data as a string, but it also accepts a path or an open file where to write JSON data along with configuration options related to JSON formatting:

If you would rather work with Python data and then convert that to JSON or some other format yourself, you can use TestSuite.to_dict instead.

Creating suite from JSON

A suite can be constructed from JSON data using the TestSuite.from_json method. It works both with JSON strings and paths to JSON files:

If you have data as a Python dictionary, you can use TestSuite.from_dict instead. Regardless of how a suite is recreated, it exists only in memory and original data files on the file system are not recreated.

As the above example demonstrates, the created suite can be executed using the TestSuite.run method. It may, however, be easier to execute a JSON file directly as explained in the following section.

Executing JSON files

When executing tests or tasks using the robot command, JSON files with the custom .rbt extension are parsed automatically. This includes running individual JSON files like robot tests.rbt and running directories containing .rbt files. If you would rather use the standard .json extension, you need to configure which files are parsed .

Adjusting suite source

Suite source in the data got from TestSuite.to_json and TestSuite.to_dict is in absolute format. If a suite is recreated later on a different machine, the source may thus not match the directory structure on that machine. To avoid that, it is possible to use the TestSuite.adjust_source method to make the suite source relative before getting the data and add a correct root directory after the suite is recreated:

JSON structure

Imports, variables and keywords created in suite files are included in the generated JSON along with tests and tasks. The exact JSON structure is documented in the running.json schema file .

When Robot Framework parses the test data files, it ignores:

  • All data before the first test data section .
  • Data in the Comments section.
  • All empty rows.
  • All empty cells at the end of rows when using the pipe separated format .
  • 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 .

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.

Escaping special characters

The backslash character can be used to escape special characters so that their literal values are used.

Escaping special characters
Character Meaning Examples
Dollar sign, never starts a .
At sign, never starts a .
Ampersand, never starts a .
Percent sign, never starts an .
Hash sign, never starts a .
Equal sign, never part of .
Pipe character, not a separator in the .
Backslash character, never escapes anything.

Forming escape sequences

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
Newline character.
Carriage return character
Tab character.
Character with hex value .
Character with hex value .
Character with hex value .

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.

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.

Built-in variable ${\n} can be used if operating system dependent line terminator is needed ( \r\n on Windows and \n elsewhere).

Handling empty values

When using the space separated format , the number of spaces used as a separator can vary and thus empty values cannot be recognized unless they are 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.

When using the pipe separated format , empty values need to be escaped only when they are at the end of the row:

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 values, it is possible to do that either by using the backslash character or by using the built-in variable ${SPACE} .

Escaping spaces examples
Escaping with backslash Escaping with Notes
\ leading space  
trailing space \ Backslash must be after the space.
\ \ Backslash needed on both sides.
consecutive \ \ spaces Using .

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 is possible to split it and start continuing rows with ellipsis ( ... ). Ellipses can be indented to match the indentation of the starting row and they must always be followed by the normal test data separator.

In most places split lines have exact same semantics as lines that are not split. Exceptions to this rule are suite , test and keyword documentation as well suite metadata . With them split values are automatically joined together with the newline character to ease creating multiline values.

The ... syntax allows also splitting variables in the Variable section . When long scalar variables (e.g. ${STRING} ) are split to multiple rows, the final value is got by concatenating the rows together. The separator is a space by default, but that can be changed by starting the value with SEPARATOR=<sep> .

Splitting lines is illustrated in the following two examples containing exactly same data without and with splitting.

Robot Framework localization efforts were started in Robot Framework 6.0 that allowed translation of section headers , settings , Given/When/Then prefixes used in Behavior Driven Development (BDD), and true and false strings used in automatic Boolean argument conversion. The plan is to extend localization support in the future, for example, to log and report and possibly also to control structures.

This section explains how to activate languages , what built-in languages are supported, how to create custom language files and how new translations can be contributed .

Using command line option

The main mechanism to activate languages is specifying them from the command line using the --language option. When enabling built-in languages , it is possible to use either the language name like Finnish or the language code like fi . Both names and codes are case and space insensitive and also the hyphen ( - ) is ignored. To enable multiple languages, the --language option needs to be used multiple times:

The same --language option is also used when activating custom language files . With them the value can be either a path to the file or, if the file is in the module search path , the module name:

For backwards compatibility reasons, and to support partial translations, English is always activated automatically. Future versions may allow disabling it.

Pre-file configuration

It is also possible to enable languages directly in data files by having a line Language: <value> (case-insensitive) before any of the section headers. The value after the colon is interpreted the same way as with the --language option:

If there is a need to enable multiple languages, the Language: line can be repeated. These configuration lines cannot be in comments so something like # Language: Finnish has no effect.

Due to technical limitations, the per-file language configuration affects also parsing subsequent files as well as the whole execution. This behavior is likely to change in the future and should not be relied upon. If you use per-file configuration, use it with all files or enable languages globally with the --language option.

The following languages are supported out-of-the-box. Click the language name to see the actual translations:

  • Bulgarian (bg)
  • Bosnian (bs)
  • German (de)
  • Spanish (es)
  • Finnish (fi)
  • French (fr)
  • Italian (it)
  • Japanese (ja)
  • Korean (ko)
  • Polish (pl)
  • Portuguese (pt)
  • Brazilian Portuguese (pt-BR)
  • Romanian (ro)
  • Russian (ru)
  • Swedish (sv)
  • Turkish (tr)
  • Ukrainian (uk)
  • Vietnamese (vi)
  • Chinese Simplified (zh-CN)
  • Chinese Traditional (zh-TW)

All these translations have been provided by the awesome Robot Framework community. If a language you are interested in is not included, you can consider contributing it!

If a language you would need is not available as a built-in language, or you want to create a totally custom language for some specific need, you can easily create a custom language file. Language files are Python files that contain one or more language definitions that are all loaded when the language file is taken into use. Language definitions are created by extending the robot.api.Language base class and overriding class attributes as needed:

Assuming the above code would be in file example.py , a path to that file or just the module name example could be used when the language file is activated .

The above example adds only some of the possible translations. That is fine because English is automatically enabled anyway. Most values must be specified as strings, but BDD prefixes and true/false strings allow more than one value and must be given as lists. For more examples, see Robot Framework's internal languages module that contains the Language class as well as all built-in language definitions.

If you want to add translation for a new language or enhance existing, head to Crowdin that we use for collaboration. For more details, see the separate Localization project, and for questions and free discussion join the #localization channel on our Slack .

Robot Framework syntax creates a simple programming language, and similarly as with other languages, it is important to think about the coding style. Robot Framework syntax is pretty flexible on purpose, but there are some generally recommended conventions:

  • Four space indentation.
  • Four space separation between keywords and arguments, settings and their values, etc... In some cases it makes sense to use more than four spaces. For example when aligning values in the Settings or Variables section or in data-driven style .
  • Global variables using capital letters like ${EXAMPLE} and local variables using lower-case letters like ${example} .
  • Consistency within a single file and preferably within the whole project.

One case where there currently is no strong convention is keyword capitalization. Robot Framework itself typically uses title case like Example Keyword in documentation and elsewhere, and this style is often used in Robot Framework data as well. It does not work too well with longer, sentence-like keywords such as Log into system as an admin , though.

Teams and organizations using Robot Framework should have their own coding standards. The community developed Robot Framework Style Guide is an excellent starting point that can be amended as needed. It is also possible to enforce these conventions by using the Robocop linter and the Robotidy code formatter.

This section describes the overall test case syntax. Organizing test cases into test suites using suite files and suite directories is discussed in the next section.

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

Settings in the test case section, test case related settings in the setting section, positional arguments, default values, variable number of arguments, named arguments, free named arguments, named-only arguments, arguments embedded to keyword names, when test case fails, error messages, deprecation of force tags and default tags, reserved tags, basic usage, templates with embedded arguments, templates with for loops, templates with if/else structures, keyword-driven style, data-driven style, behavior-driven style.

Test cases are constructed in test case sections from the available keywords. Keywords can be imported from test libraries or resource files , or created in the keyword section of the test case file itself.

The first column in the test case section 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 section. It is an error to have something between the section 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.

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.

Setting names are case-insensitive, but the format used above is recommended. Settings used to be also space-insensitive, but that was deprecated in Robot Framework 3.1 and trying to use something like [T a g s] causes an error in Robot Framework 3.2. Possible spaces between brackets and the name (e.g. [ Tags ] ) are still allowed.

Example test case with settings:

The Setting section can have the following test case related settings. These settings are mainly default values for the test case specific settings listed earlier.

The earlier examples have already demonstrated keywords taking different arguments, and this section discusses this important functionality more thoroughly. How to actually implement user keywords and library keywords with different arguments is discussed in separate sections.

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 pydoc .

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.

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 . 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.

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.

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.

Named arguments with variables

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.

Escaping named arguments syntax

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.

Where named arguments are supported

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 exceptions are Python keywords explicitly using positional-only arguments .

Named arguments example

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.

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.

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 .

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.

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.

The test case name comes directly from the Test Case section: it is exactly what is entered into the test case column. Test cases in one test suite should have unique names. Pertaining to this, you can also use the automatic variable ${TEST_NAME} within the test itself to refer to the test name. It is available whenever a test is being executed, including all user keywords, as well as the test setup and the test teardown.

Starting from Robot Framework 3.2, possible variables in the test case name are resolved so that the final name will contain the variable value. If the variable does not exist, its name is left unchanged.

The [Documentation] setting allows setting free form documentation for a test case. That text is shown in the command line output and in the resulting logs and reports. If documentation gets long, it can be split into multiple rows . It is possible to use simple HTML formatting and variables can be used to make the documentation dynamic. Possible non-existing variables are left unchanged.

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 .

Using tags in Robot Framework is a simple, yet powerful mechanism for classifying test cases and also user keywords . Tags are free text and Robot Framework itself has no special meaning for them except for the reserved tags discussed below. Tags can be used at least for the following purposes:

  • They 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 and skipped) are automatically collected based on them.
  • They can be used to include and exclude as well as to skip test cases.

There are multiple ways how to specify tags for test cases explained below:

As the example shows, tags can be created using variables, but otherwise they preserve the exact name used in the data. When tags are compared, for example, to collect statistics, to select test to be executed, or to remove duplicates, comparisons are case, space and underscore insensitive.

As demonstrated by the above examples, removing tags using -tag syntax supports simple patterns like -requirement: * . Tags starting with a hyphen have no special meaning otherwise than with the [Tags] setting. If there is a need to set a tag starting with a hyphen with [Tags] , it is possible to use the escaped format like \-tag .

The Test Tags setting is new in Robot Framework 6.0. Earlier versions support Force Tags and Default Tags settings discussed in the next section.

The -tag syntax for removing common tags is new in Robot Framework 7.0.

Prior to Robot Framework 6.0, tags could be specified to tests in the Setting section using two different settings:

Both of these settings still work, but they are considered deprecated. A visible deprecation warning will be added in the future, most likely in Robot Framework 8.0, and eventually these settings will be removed. Tools like Tidy can be used to ease transition.

Updating Force Tags requires only renaming it to Test Tags . The Default Tags setting will be removed altogether, but the -tag functionality introduced in Robot Framework 7.0 provides same underlying functionality. The following examples demonstrate the needed changes.

Old syntax:

New syntax:

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. The current reserved tags are listed below, but more such tags are likely to be added in the future.

As of RobotFramework 4.1, reserved tags are suppressed by default in tag statistics . They will be shown when they are explicitly included via the --tagstatinclude robot:* command line option.

Robot Framework has similar test setup and teardown functionality as many other test automation frameworks. In short, a test setup is something that is executed before a test case, and a test teardown is executed after a test case. In Robot Framework setups and teardowns are just normal keywords with possible arguments.

A setup and a 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 section. Individual test cases can also have their own setup or teardown. They are defined with the [Setup] or [Teardown] settings in the test case section 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.

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 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.

Test templates convert normal keyword-driven test cases into data-driven tests. Whereas the body of a keyword-driven test case is constructed from keywords and their possible arguments, test cases with template contain only the arguments for the template keyword. Instead of repeating the same keyword multiple times per test and/or with all tests in a file, it is possible to use it only per test or just once per file.

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.

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 section, 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 section, 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 keywords with default values or accepting variable number of arguments , 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:

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.

IF/ELSE structures can be also used together with templates. This can be useful, for example, when used together with FOR loops to filter executed arguments.

There are several different ways in which test cases may be written. Test cases that describe some kind of workflow may be written either in keyword-driven or behavior-driven style. Data-driven style can be used to test the same workflow with varying input data.

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 ).

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.

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.

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.

Ignoring Given/When/Then/And/But prefixes

Prefixes Given , When , Then , And and But can be omitted when creating keywords. For example, Given login page is open in the above example is typically implemented without the word Given so that the name is just Login page is open . Omitting prefixes allows using the same keyword with different prefixes. For example, Welcome page should be open could be used as Then welcome page should be open or and welcome page should be open .

These prefixes can be localized . See the Translations appendix for supported translations.

Embedding data to keywords

When writing concrete examples it is useful to be able to pass actual data to keyword implementations. This can be done by embedding arguments into keyword name .

In addition to test automation, Robot Framework can be used for other automation purposes, including robotic process automation (RPA). It has always been possible, but Robot Framework 3.1 added official support for automating tasks , not only tests. For most parts creating tasks works the same way as creating tests and the only real difference is in terminology. Tasks can also be organized into suites exactly like test cases.

Tasks are created based on the available keywords exactly like test cases, and the task syntax is in general identical to the test case syntax . The main difference is that tasks are created in Task sections instead of Test Case sections:

It is an error to have both tests and tasks in same file.

Settings that can be used in the task section are exactly the same as in the test case section . In the setting section it is possible to use Task Setup , Task Teardown , Task Template and Task Timeout instead of their Test variants.

Robot Framework test cases are created in test case files, which can be organized into directories. These files and directories create a hierarchical test suite structure. Same concepts apply also when creating tasks , but the terminology differs.

Suite initialization files

Robot Framework test cases are created using test case sections in suite files, also known as test case files. Such a file automatically creates a test suite from all the test cases it contains. There is no upper limit for how many test cases there can be, but it is recommended to have less than ten, unless the data-driven approach is used, where one test case consists of only one high-level keyword.

The following settings in the Setting section can be used to customize the suite:

Setting names are case-insensitive, but the format used above is recommended.

Test case files can be organized into directories, and these directories create higher-level test suites. A test suite created from a directory cannot have any test cases directly, but it contains other test suites with test cases, instead. These directories can then be placed into other directories creating an even higher-level suite. There are no limits for the structure, so test cases can be organized as needed.

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.

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.

Starting from Robot Framework 6.1, it is also possible to define a suite initialization file for automatically created suite when starting the test execution by giving multiple paths .

Initialization files have the same structure and syntax as test case files, except that they cannot have test case sections 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 suite files , but setting some test case related settings is also possible. How to use different settings in the initialization files is explained below.

The test suite name is constructed from the file or directory name by default. The name is created so that the extension is ignored, possible underscores are replaced with spaces, and names fully in lower case are title cased. For example, some_tests.robot becomes Some Tests and My_test_directory becomes My test directory .

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.

Starting from Robot Framework 6.1, it is also possible to give a custom name to a suite by using the Name setting in the Setting section:

The name of the top-level suite can be overridden from the command line with the --name option.

The documentation for a test suite is set using the Documentation setting in the Settings section. It can be used both in suite files and in suite initialization files . Suite documentation has exactly the same characteristics regarding to where it is shown and how it can be created as test case documentation . For details about the syntax see the Documentation formatting appendix.

The documentation of the top-level suite can be overridden from the command line with the --doc option.

In addition to documentation, suites can also have free metadata. This metadata is defined as name-value pairs in the Settings section using the Metadata setting. It is shown in reports and logs similarly as documentation.

Name of the metadata is the first argument given to the Metadata setting and the remaining arguments specify its value. The value is handled similarly as documentation, which means that it supports HTML formatting and variables , and that longer values can be split into multiple rows .

The free metadata of the top-level suite can be set from the command line with the --metadata option.

Not only test cases but also test suites can have a setup and a teardown. A suite setup is executed before running any of the suite's test cases or child test suites, and a suite teardown is executed after them. All test suites can have a setup and a teardown; with suites created from a directory they must be specified in a suite initialization file .

Similarly as with test cases, a suite setup and teardown are keywords that may take arguments. They are defined in the Setting section 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.

Test libraries contain those lowest-level keywords, often called library keywords , which actually interact with the system under test. All test cases always use keywords from some library, often through higher-level user keywords . This section explains how to take test libraries into use and how to use the keywords they provide. Creating test libraries is described in a separate section.

Using Library setting

Using import library keyword, using library name, using physical path to library, normal standard libraries, remote library.

Test libraries are typically imported using the Library setting, but it is also possible to use the Import Library keyword.

Test libraries are normally imported using the Library setting in the Setting section 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 suite files , resource files and 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.

Libraries to import can be specified either by using the library name or the path to the library. These approaches work the same way regardless if the library is imported using the Library setting or the Import Library keyword.

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, i.e. .py . If a 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 .

The library name is shown in test logs before keyword names, and if multiple keywords have the same name, they must be used so that the keyword name is prefixed with the library name . The library name is got normally from the module or class name implementing it, but there are some situations where changing it is desirable:

  • 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.
  • 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 AS (case-sensitive) after the library name and then having the new name after that. 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 between the original library name and the AS marker. 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 section and when using the Import Library keyword.

Prior to Robot Framework 6.0 the marker to use when giving a custom name to a library was WITH NAME instead of AS . The old syntax continues to work, but it is considered deprecated and will eventually be removed.

Some test libraries are distributed with Robot Framework and these libraries are called standard libraries . The BuiltIn library is special, because it is taken into use automatically and thus its keywords are always available. Other standard libraries need to be imported in the same way as any other libraries, but there is no need to install them.

The available normal standard libraries are listed below with links to their documentations:

BuiltIn Collections DateTime Dialogs OperatingSystem Process Screenshot String Telnet XML

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.

Any test library that is not one of the standard libraries is, by definition, an external library . The Robot Framework open source community has implemented several generic libraries, such as SeleniumLibrary and SwingLibrary , which are not packaged with the core framework. A list of publicly available libraries can be found from http://robotframework.org .

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.

Scalar variable syntax

List variable syntax, dictionary variable syntax, accessing list and dictionary items, environment variables, variable section, variable file, setting variables in command line, return values from keywords, using set test/suite/global variable keywords, operating-system variables, number variables, boolean and none/null variables, space and empty variables, automatic variables, variable priorities, variable scopes, extended variable syntax, extended variable assignment, variables inside variables, inline python evaluation.

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 and Keyword sections, 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} .

Variables are useful, for example, in these cases:

  • 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} .

This section explains how to use variables, including the normal scalar variable syntax ${var} , how to use variables in list and dictionary contexts like @{var} and &{var} , respectively, and how to use environment variables like %{var} . Different ways how to create variables are discussed in the subsequent sections.

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.

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.

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 Python object:

With these two variables set, we then have the following test data:

Finally, when this test data is executed, different keywords receive the arguments as explained below:

  • KW 1 gets a string Hello, world!
  • KW 2 gets an object stored to variable ${OBJ}
  • KW 3 gets a string I said "Hello, world!"
  • KW 4 gets a string You said "Hi, terra!"

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 the list is expanded and individual items are passed in as separate arguments. This is easiest to explain with an example. Assuming that a variable @{USER} has value ['robot', 'secret'] , the following two test cases are equivalent:

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.

Starting from Robot Framework 4.0, list expansion can be used in combination with list item access making these usages possible:

Using list variables with other data

It is possible to use list variables with other arguments, including other list variables.

Using list variables with settings

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 the dictionary is expanded and individual items 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.

Starting from Robot Framework 4.0, dictionary expansion can be used in combination with dictionary item access making usages like &{nested}[key] possible.

Using dictionary variables with other data

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.

Using dictionary variables with settings

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 subscriptable variables, e.g. lists and dictionaries, using special syntax like ${var}[item] or ${var}[nested][item] . Starting from Robot Framework 4.0, it is also possible to use item access together with list expansion and dictionary expansion by using syntax @{var}[item] and &{var}[item] , respectively.

Prior to Robot Framework 3.1 the normal item access syntax was @{var}[item] with lists and &{var}[item] with dictionaries. Robot Framework 3.1 introduced the generic ${var}[item] syntax along with some other nice enhancements and the old item access syntax was deprecated in Robot Framework 3.2.

Accessing sequence items

It is possible to access a certain item of a variable containing a sequence (e.g. list, string or bytes) 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.

Sequence 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 sequence. Same way as with Python you can specify the start index, the end index, and the step:

The slice syntax is new in Robot Framework 3.1. It was extended to work with list expansion like @{var}[1:] in Robot Framework 4.0.

Prior to Robot Framework 3.2, item and slice access was only supported with variables containing lists, tuples, or other objects considered list-like. Nowadays all sequences, including strings and bytes, are supported.

Accessing individual dictionary items

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.

Nested item access

Also nested subscriptable variables 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:

Robot Framework allows using environment variables in the test data using the syntax %{ENV_VAR_NAME} . They are limited to string values. It is possible to specify a default value, that is used if the environment variable does not exists, by separating the variable name and the default value with an equal sign like %{ENV_VAR_NAME=default value} .

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.

Support for specifying the default value is new in Robot Framework 3.2.

Variables can spring into existence from different sources.

The most common source for variables are Variable sections in suite files and resource files . Variable sections 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.

Creating scalar variables

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 section 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.

It is also possible, but not obligatory, to use the equals sign = after the variable name to make assigning variables slightly more explicit.

If a scalar variable has a long value, it can be split into multiple rows by using the ... syntax. By default rows are concatenated together using a space, but this can be changed by using a having separator configuration option after the last value:

The separator option is new in Robot Framework 7.0, but also older versions support configuring the separator. With them the first value can contain a special SEPARATOR marker:

Both the separator option and the SEPARATOR marker are case-sensitive. Using the separator option is recommended, unless there is a need to support also older versions.

Creating list variables

Creating list variables is as easy as creating scalar variables. Again, the variable name is in the first column of the Variable section 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 .

Creating dictionary variables

Dictionary variables can be created in the Variable section 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.

With nested dictionary variables keys are accessible 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] .

Creating variable name based on another variable

Starting from Robot Framework 7.0, it is possible to create the variable name dynamically based on another variable:

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 section 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.

In the examples above, variables are set so that

  • ${EXAMPLE} gets the value value
  • ${HOST} and ${USER} get the values localhost:7272 and 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.

Assigning scalar variables

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.

Assigning variables with item values

Starting from Robot Framework 6.1, when working with variables that support item assignment such as lists or dictionaries, it is possible to set their values by specifying the index or key of the item using the syntax ${var}[item] where the item part can itself contain a variable:

Starting from Robot Framework 7.0, it is possible to create the name of the assigned variable dynamically based on another variable:

Assigning list variables

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.

Assigning dictionary variables

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 section. 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:

  • ${a} , ${b} and ${c} with values 1 , 2 , and 3 , respectively.
  • ${first} with value 1 , and @{rest} with value [2, 3] .
  • @{before} with value [1, 2] and ${last} with value 3 .
  • ${begin} with value 1 , @{middle} with value [2] and ${end} with value 3 .

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.

Automatically logging assigned variable value

To make it easier to understand what happens during execution, the beginning of value that is assigned is automatically logged. The default is to show 200 first characters, but this can be changed by using the --maxassignlength command line option when running tests. If the value is zero or negative, the whole assigned value is hidden.

The reason the value is not logged fully is that it could be really big. If you always want to see a certain value fully, it is possible to use the BuiltIn Log keyword to log it after the assignment.

The --maxassignlength option is new in Robot Framework 5.0.

Starting from Robot Framework 7.0, it is possible to create variables inside tests and user keywords using the VAR syntax. The VAR marker is case-sensitive and it must be followed by a variable name and value. Other than the mandatory VAR , the overall syntax is mostly the same as when creating variables in the Variable section .

The new syntax is aims to make creating variables simpler and more uniform. It is especially indented to replace the BuiltIn keywords Set Variable , Set Test Variable , Set Suite Variable and Set Global Variable , but it can be used instead of Catenate , Create List and Create Dictionary as well.

In simple cases scalar variables are created by just giving a variable name and its value. The value can be a hard-coded string or it can itself contain a variable. If the value is long, it is possible to split it into multiple columns and rows. In that case parts are joined together with a space by default, but the separator to use can be specified with the separator configuration option. It is possible to have an optional = after the variable name the same way as when creating variables based on return values from keywords and in the Variable section .

Creating list and dictionary variables

List and dictionary variables are created similarly as scalar variables. When creating dictionaries, items must be specified using the name=value syntax.

Variables created with the VAR syntax are are available only within the test or user keyword where they are created. That can, however, be altered by using the scope configuration option. Supported values are:

Although Robot Framework variables are case-insensitive, it is recommended to use capital letters with non-local variable names.

Creating variables conditionally

The VAR syntax works with IF/ELSE structures which makes it easy to create variables conditionally. In simple cases using inline IF can be convenient.

If there is a need, variable name can also be created dynamically based on another variable.

The VAR syntax is recommended over these keywords when using Robot Framework 7.0 or newer.

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. It is an error to call Set Test Variable outside the scope of a test (e.g. in a Suite Setup or Teardown).

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 section 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.

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 .

Robot Framework provides some built-in variables that are available automatically.

Built-in variables related to the operating system ease making the test data operating-system-agnostic.

Available operating-system-related built-in variables
Variable Explanation
${CURDIR} An absolute path to the directory where the test data file is located. This variable is case-sensitive.
${TEMPDIR} An absolute path to the system temporary directory. In UNIX-like systems this is typically /tmp, and in Windows c:\Documents and Settings\<user>\Local Settings\Temp.
${EXECDIR} An absolute path to the directory where test execution was started from.
${/} The system directory path separator. in UNIX-like systems and \ in Windows.
${:} The system path element separator. in UNIX-like systems and in Windows.
${\n} The system line separator. \n in UNIX-like systems and \r\n in Windows.

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.

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 can be created using the variable syntax similarly as numbers.

These variables are case-insensitive, so for example ${True} and ${true} are equivalent.

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.

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.

${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).

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.

Available automatic variables
Variable Explanation Available
${TEST NAME} The name of the current test case. Test case
@{TEST TAGS} Contains the tags of the current test case in alphabetical order. Can be modified dynamically using Set Tags and Remove Tags keywords. Test case
${TEST DOCUMENTATION} The documentation of the current test case. Can be set dynamically using using Set Test Documentation keyword. Test case
${TEST STATUS} The status of the current test case, either PASS or FAIL.
${TEST MESSAGE} The message of the current test case.
${PREV TEST NAME} The name of the previous test case, or an empty string if no tests have been executed yet. Everywhere
${PREV TEST STATUS} The status of the previous test case: either PASS, FAIL, or an empty string when no tests have been executed. Everywhere
${PREV TEST MESSAGE} The possible error message of the previous test case. Everywhere
${SUITE NAME} The full name of the current test suite. Everywhere
${SUITE SOURCE} An absolute path to the suite file or directory. Everywhere
${SUITE DOCUMENTATION} The documentation of the current test suite. Can be set dynamically using using Set Suite Documentation keyword. Everywhere
&{SUITE METADATA} The free metadata of the current test suite. Can be set using Set Suite Metadata keyword. Everywhere
${SUITE STATUS} The status of the current test suite, either PASS or FAIL.
${SUITE MESSAGE} The full message of the current test suite, including statistics.
${KEYWORD STATUS} The status of the current keyword, either PASS or FAIL.
${KEYWORD MESSAGE} The possible error message of the current keyword.
${LOG LEVEL} Current . Everywhere
${OUTPUT DIR} An absolute path to the as a string. Everywhere
${OUTPUT FILE} An absolute path to the as a string or a string if the output file is not created. Everywhere
${LOG FILE} An absolute path to the as a string or a string if the log file is not created. Everywhere
${REPORT FILE} An absolute path to the as a string or a string if the report file is not created. Everywhere
${DEBUG FILE} An absolute path to the as a string or a string if the debug file is not created. Everywhere
&{OPTIONS}

A dictionary exposing command line options. The dictionary keys match the command line options and can be accessed both like and . Available options:

(--exclude) (--include) (--skip) (--skip-on-failure) (--console-width)

itself was added in RF 5.0 and in RF 7.1. More options can be exposed later.

Everywhere

Suite related variables ${SUITE SOURCE} , ${SUITE NAME} , ${SUITE DOCUMENTATION} and &{SUITE METADATA} as well as options related to command line options like ${LOG FILE} and &{OPTIONS} are available already when libraries and variable files are imported. Possible variables in these automatic variables are not yet resolved at the import time, though.

Variables coming from different sources have different priorities and are available in different scopes.

Variables from the command line

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 sections 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 section in a test case file

Variables created using the Variable section 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 sections are available in all other sections in the file where they are created. This means that they can be used also in the Setting section, for example, for importing more variables from resource and variable files.

Imported 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 section 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 section of the file that imports them. This is due to the Variable section being processed before the Setting section where the resource files and variable files are imported.

Variables set during test execution

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 section 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.

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 by using the VAR syntax or the Set Global Variable keyword anywhere in the test data. Additionally also built-in variables are global.

It is recommended to use capital letters with all global variables.

Test suite scope

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 sections, imported from resource and variable files , or set during the test execution using the VAR syntax or the Set Suite Variable keyword.

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.

Test case scope

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 VAR syntax or the Set Test Variable keyword anywhere in a test case. Trying to create test variables in suite setup or suite teardown causes and error.

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 with the VAR syntax , and user keywords also get them as arguments .

It is recommended to use lower-case letters with local variables.

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:

When this test data is executed, the keywords get the arguments as explained below:

  • KW 1 gets string Robot
  • KW 2 gets string Robot eats Cucumber
  • KW 3 gets string two

The extended variable syntax is evaluated in the following order:

  • The variable is searched using the full variable name. The extended variable syntax is evaluated only if no matching variable is found.
  • 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.
  • A variable matching the body is searched. If there is no match, an exception is raised and the test case fails.
  • 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.
  • The whole extended variable is replaced with the value returned from the evaluation.

Many standard Python objects, including strings and numbers, have methods that can be used with the extended variable syntax either explicitly or implicitly. Sometimes this can be really useful and reduce the need for setting temporary variables, but it is also easy to overuse it and create really cryptic test data. Following examples show few pretty good usages.

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:

  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.

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.

Variable syntax can also be used for evaluating Python expressions. The basic syntax is ${{expression}} i.e. there are double curly braces around the expression. The expression can be any valid Python expression such as ${{1 + 2}} or ${{['a', 'list']}} . Spaces around the expression are allowed, so also ${{ 1 + 2 }} and ${{ ['a', 'list'] }} are valid. In addition to using normal scalar variables , also list variables and dictionary variables support @{{expression}} and &{{expression}} syntax, respectively.

Main usages for this pretty advanced functionality are:

  • Evaluating Python expressions involving Robot Framework's variables ( ${{len('${var}') > 3}} , ${{$var[0] if $var is not None else None}} ).
  • Creating values that are not Python base types ( ${{decimal.Decimal('0.11')}} , ${{datetime.date(2019, 11, 5)}} ).
  • Creating values dynamically ( ${{random.randint(0, 100)}} , ${{datetime.date.today()}} ).
  • Constructing collections, especially nested collections ( ${{[1, 2, 3, 4]}} , ${{ {'id': 1, 'name': 'Example', 'children': [7, 9]} }} ).
  • Accessing constants and other useful attributes in Python modules ( ${{math.pi}} , ${{platform.system()}} ).

This is somewhat similar functionality than the extended variable syntax discussed earlier. As the examples above illustrate, this syntax is even more powerful as it provides access to Python built-ins like len() and modules like math . In addition to being able to use variables like ${var} in the expressions (they are replaced before evaluation), variables are also available using the special $var syntax during evaluation. The whole expression syntax is explained in the Evaluating expressions appendix.

Instead of creating complicated expressions, it is often better to move the logic into a custom library . That eases maintenance, makes test data easier to understand and can also enhance execution speed.

The inline Python evaluation syntax is new in Robot Framework 3.2.

Keyword sections are used to create new higher-level keywords by combining existing keywords together. These keywords are called user keywords to differentiate them from lowest level library keywords that are implemented in test libraries. The syntax for creating user keywords is very close to the syntax for creating test cases, which makes it easy to learn.

Settings in the Keyword section

Positional arguments with user keywords, default values with user keywords, variable number of arguments with user keywords, free named arguments with user keywords, named-only arguments with user keywords, embedded arguments matching wrong values, resolving conflicts, using custom regular expressions, behavior-driven development example, using return statement, using [return] setting, using special keywords to return.

In many ways, the overall user keyword syntax is identical to the test case syntax . User keywords are created in Keyword sections which differ from Test Case sections 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 suite files , resource files , and 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.

The format used above is recommended, but setting names are case-insensitive and spaces are allowed between brackets and the name. For example, [ TAGS ] :setting is valid.

The user keyword name is defined in the first column of the Keyword section. Of course, the name should be descriptive, and it is acceptable to have quite long keyword names. Actually, when creating use-case-like test cases, the highest-level keywords are often formulated as sentences or even paragraphs.

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 .

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.

Prior to Robot Framework 3.1, the short documentation contained only the first physical line of the keyword documentation.

Both user keywords and library keywords can have tags. Similarly as when tagging test cases , there are two settings affecting user keyword tags:

Keyword tags can be specified using variables, the -tag syntax supports patterns, and so on, exactly as test case tags .

In addition to using the dedicated settings, keyword tags can be specified on the last line of the documentation with Tags: prefix so that tags are separated with a comma. For example, following two keywords get same three tags:

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 the robot: prefix 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. Starting from Robot Framework 6.1, flattening keyword during execution time can be taken into use using reserved tag robot:flatten .

Keyword Tags is new in Robot Framework 6.0. With earlier versions all keyword tags need to be specified using the [Tags] setting.

Most user keywords need to take some arguments. The syntax for specifying them is probably the most complicated feature normally needed with Robot Framework, but even that is relatively easy, particularly in most common cases. Arguments are normally specified with the [Arguments] setting, and argument names use the same syntax as variables , for example ${arg} .

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} .

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. Default value can also be defined based on earlier arguments accepted by the keyword.

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.

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:

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.

The previous section explained how to pass arguments to keywords so that they are listed separately after the keyword name. Robot Framework has also another approach to pass arguments, embedding them directly to the keyword name, used by the second test below:

As the example illustrates, embedding arguments to keyword names can make the data easier to read and understand even for people without any Robot Framework experience.

The previous example showed how using a keyword Select cat from list is more fluent than using Select from list so that cat is passed to it as an argument. We obviously could implement Select cat from list as a normal keyword accepting no arguments, but then we needed to implement various other keywords like Select dog from list for other animals. Embedded arguments simplify this and we can instead implement just one keyword with name Select ${animal} from list and use it with any animal:

As the above example shows, embedded arguments are specified simply by using variables in keyword names. The arguments used in the name are naturally available inside the keyword and they have different values depending on how the keyword is called. In the above example, ${animal} has value cat when the keyword is used for the first time and dog when it is used for the second time.

Starting from Robot Framework 6.1, it is possible to create user keywords that accept both embedded and "normal" arguments:

Other than the special name, keywords with embedded arguments are created just like other user keywords. They are also used the same way as other keywords except that spaces and underscores are not ignored in their names when keywords are matched. They are, however, case-insensitive like other keywords. For example, the Select ${animal} from list keyword could be used like select cow from list , but not like Select cow fromlist .

Embedded arguments do not support default values or variable number of arguments like normal arguments do. If such functionality is needed, normal arguments should be used instead. Passing embedded arguments as variables is possible, but that can reduce readability:

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, Select Los Angeles Lakers in the following example matches Select ${city} ${team} so that ${city} contains Los and ${team} contains Angeles Lakers :

An easy solution to this problem is surrounding arguments with double quotes or other characters not used in the actual values. This fixed example works so that cities and teams match correctly:

This approach is not enough to resolve all conflicts, but it helps in common cases and is generally recommended. Another benefit is that it makes arguments stand out from rest of the keyword.

Prior to Robot Framework 7.1, embedded arguments starting the keyword name also matched possible given/when/then/and/but prefixes typically used in Behavior Driven Development (BDD). For example, ${name} goes home matched Given Janne goes home so that ${name} got value Given Janne . Nowadays the prefix is ignored and ${name} will be Janne as expected. If older Robot Framework versions need to be supported, it is easiest to quote the argument like in "${name}" goes home to get consistent behavior.

An alternative solution for limiting what values arguments match is using custom regular expressions .

When using embedded arguments, it is pretty common that there are multiple keyword implementations that match the keyword that is used. For example, Execute "ls" with "lf" in the example below matches both of the keywords. It matching Execute "${cmd}" with "${opts}" is pretty obvious and what we want, but it also matches Execute "${cmd}" so that ${cmd} matches ls" with "-lh .

When this kind of conflicts occur, Robot Framework tries to automatically select the best match and use that. In the above example, Execute "${cmd}" with "${opts}" is considered a better match than the more generic Execute "${cmd}" and running the example thus succeeds without conflicts.

It is not always possible to find a single match that is better than others. For example, the second test below fails because Robot Framework matches both of the keywords equally well. This kind of conflicts need to be resolved manually either by renaming keywords or by using custom regular expressions .

Keywords that accept only "normal" arguments or no arguments at all are considered to match better than keywords accepting embedded arguments. For example, if the following keyword is added to the above example, Robot Framework used by the latter test matches it and the test succeeds:

Before looking which match is best, Robot Framework checks are some of the matching keywords implemented in the same file as the caller keyword. If there are such keywords, they are given precedence over other keywords. Alternatively, library search order can be used to control the order in which Robot Framework looks for keywords in resources and libraries.

Automatically resolving conflicts if multiple keywords with embedded arguments match is a new feature in Robot Framework 6.0. With older versions custom regular expressions explained below can be used instead.

When keywords with embedded arguments are called, the values are matched internally using regular expressions (regexps for short). The default logic goes so that every argument in the name is replaced with a pattern .*? that matches any string and tries to match as little as possible. This logic works fairly well normally, but as discussed above, sometimes keywords match wrong values and sometimes there are conflicts that cannot be resolved . A solution in these cases is specifying 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 following examples. Notice that the first one shows how the earlier problem with Select ${city} ${team} not matching Select Los Angeles Lakers properly can be resolved without quoting. That is achieved by implementing the keyword so that ${team} can only contain non-whitespace characters.

Supported regular expression syntax

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. If the regular expression syntax is invalid, creating the keyword fails with an error visible in test execution errors .

Regular expressions use the backslash character ( \ ) heavily both to form special sequences (e.g. \d ) and to escape characters that have a special meaning in regexps (e.g. \$ ). Typically in Robot Framework data backslash characters need to be escaped with another backslash, but that is not required in this context. If there is a need to have a literal backslash in the pattern, then the backslash must be escaped like ${path:c:\\temp\\.*} .

Possible lone opening and closing curly braces in the pattern must be escaped like ${open:\{} and ${close:\}} or otherwise Robot Framework is not able to parse the variable syntax correctly. If there are matching braces like in ${digits:\d{2}} , escaping is not needed.

Prior to Robot Framework 3.2, it was mandatory to escape all closing curly braces in the pattern like ${digits:\d{2\}} . This syntax is unfortunately not supported by Robot Framework 3.2 or newer and keywords using it must be updated when upgrading.

Prior to Robot Framework 6.0, using literal backslashes in the pattern required double escaping them like ${path:c:\\\\temp\\\\.*} . Patterns using literal backslashes need to be updated when upgrading.

Using variables with custom embedded argument regular expressions

When embedded arguments are used with custom regular expressions, Robot Framework automatically enhances the specified regexps so that they match variables in addition to the text matching the pattern. For example, the following test case would pass using the keywords from the earlier example.

A limitation of using variables is that their actual values are not matched against custom regular expressions. As the result keywords may be called with values that their custom regexps would not allow. This behavior is deprecated starting from Robot Framework 6.0 and values will be validated in the future. For more information see issue #4462 .

A big benefit of having arguments as part of the keyword name is that it makes it easier to use higher-level sentence-like keywords when using the behavior-driven style to write tests. As the example below shows, this support is typically used in combination with the possibility to omit Given, When and Then prefixes in keyword definitions:

Embedded arguments feature in Robot Framework is inspired by how step definitions are created in the popular BDD tool Cucumber .

Similarly as library keywords, also user keywords can return values. When using Robot Framework 5.0 or newer, the recommended approach is using the native RETURN statement. The old [Return] setting was deprecated in Robot Framework 7.0 and also BuiltIn keywords Return From Keyword and Return From Keyword If are considered deprecated.

Regardless how values are returned, they can be assigned to variables in test cases and in other user keywords.

The recommended approach to return values is using the RETURN statement. It accepts optional return values and can be used with IF and inline IF structures. Its usage is easiest explained with examples:

If you want to test the above examples yourself, you can use them with these test cases:

RETURN syntax is case-sensitive similarly as IF and FOR .

RETURN is new in Robot Framework 5.0. Use approaches explained below if you need to support older versions.

The [Return] setting defines what the keyword should return after it has been executed. Although it is recommended to have it at the end of keyword where it logically belongs, its position does not affect how it is used.

An inherent limitation of the [Return] setting is that cannot be used conditionally. Thus only the first two earlier RETURN statement examples can be created using it.

The [Return] setting was deprecated in Robot Framework 7.0 and the RETURN statement should be used instead. If there is a need to support older Robot Framework versions that do not support RETURN , it is possible to use the special keywords discussed in the next section.

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 statement and the [Return] setting discussed above.

The introduction of the RETURN statement makes these keywords redundant. Examples below contain same keywords as earlier RETURN examples but these ones are more verbose:

These keywords are effectively deprecated and the RETURN statement should be used unless there is a need to support also older versions than Robot Framework 5.0. There is no visible deprecation warning when using these keywords yet, but they will be loudly deprecated and eventually removed in the future.

A user keyword can have a setup and a teardown similarly as tests . They are specified using [Setup] and [Teardown] settings, respectively, directly to the keyword having them. Unlike with tests, it is not possible to specify a common setup or teardown to all keywords in a certain file.

A setup and a teardown are always a single keyword, but they can themselves be user keywords executing multiple keywords internally. It is possible to specify them as variables, and using a special NONE value (case-insensitive) is the same as not having a setup or a teardown at all.

User keyword setup is not much different to the first keyword inside the created user keyword. The only functional difference is that a setup can be specified as a variable, but it can also be useful to be able to explicitly mark a keyword to be a setup.

User keyword teardowns are, exactly as test teardowns, executed also if the user keyword fails. They are thus very useful when needing to do something at the end of the keyword regardless of its status. To ensure that all cleanup activities are done, the continue on failure mode is enabled by default with user keyword teardowns the same way as with test teardowns.

User keyword setups are new in Robot Framework 7.0.

User keywords can be tagged with a special robot:private tag to indicate that they should only be used in the file where they are created:

Using the robot:private tag does not outright prevent using the keyword outside the file where it is created, but such usages will cause a warning. If there is both a public and a private keyword with the same name, the public one will be used but also this situation causes a warning.

Private keywords are included in spec files created by Libdoc but not in its HTML output files.

Private user keywords are new in Robot Framework 6.0.

User keywords and variables in suite files and suite initialization files can only be used in files where they are created, but resource files provide a mechanism for sharing them. The high level syntax for creating resource files is exactly the same as when creating suite files and supported file formats are the same as well. The main difference is that resource files cannot have tests.

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 or YAML, which also makes them somewhat more complicated than Variable sections .

Taking resource files into use

Resource file structure, documenting resource files, example resource file, resource files using restructured text format, resource files using json format, taking variable files into use, getting variables directly from a module, getting variables from a special function, implementing variable file as a class, variable file as yaml, variable file as json.

Resource files are typically created using the plain text format, but also reStructuredText format and JSON format are supported.

Resource files are imported using the Resource setting in the Settings section so that the path to the resource file is given as an argument to the setting. The recommended extension for resource files is .resource . For backwards compatibility reasons also .robot , .txt and .tsv work, but using .resource may be mandated in the future.

If the resource file path is absolute, it is used directly. Otherwise, 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 . Searching resource files from the module search path makes it possible to bundle them into Python packages as package data and importing them like package/example.resource .

The resource file path can contain variables, and it is recommended to use them to make paths system-independent (for example, ${RESOURCES}/login.resource or just ${RESOURCE_PATH} ). Additionally, forward slashes ( / ) in the path are automatically changed to backslashes ( \ ) on Windows.

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.

The .resource extension is new in Robot Framework 3.1.

The higher-level structure of resource files is the same as that of suite files otherwise, but they cannot contain tests or tasks. Additionally, the Setting section in resource files can contain only imports ( Library , Resource , Variables ), Documentation and Keyword Tags . The Variable section and Keyword section are used exactly the same way as in suite 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 section similarly as suites .

Libdoc and various editors 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.

The reStructuredText format that can be used with suite files works also with resource files. Such resource files can use either .rst or .rest extension and they are otherwise imported exactly as normal resource files:

When parsing resource files using the reStructuredText format, Robot Framework ignores all data outside code blocks containing Robot Framework data exactly the same way as when parsing reStructuredText suite files . For example, the following resource file imports OperatingSystem library, defines ${MESSAGE} variable and creates My Keyword keyword:

Resource files can be created using JSON the same way as suite files . Such JSON resource files must use either the standard .json extension or the custom .rsrc extension. They are otherwise imported exactly as normal resource files:

Resource files can be converted to JSON using ResourceFile.to_json and recreated using ResourceFile.from_json :

Variable files contain variables that can be used in the test data. Variables can also be created using Variable sections or set from the command line , but variable files allow creating them dynamically and also make it easy to create other variable values than strings.

Variable files are typically implemented as modules and there are two different approaches for creating variables:

Alternatively variable files can be implemented as classes that the framework will instantiate. Also in this case it is possible to create variables as attributes or get them dynamically from the get_variables method. Variable files can also be created as YAML and JSON .

Setting section

All test data files can import variable files using the Variables setting in the Setting section. Variable files are typically imported using a path to the file 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 directories in the module search path . The path can also contain variables, and slashes are converted to backslashes on Windows.

Starting from Robot Framework 5.0, variable files implemented using Python can also be imported using the module name similarly as libraries . When using this approach, the module needs to be in the module search path .

If a variable file accepts arguments , they are specified after the path or name of the variable file to import:

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 sections 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 or module name similarly as when importing them using the Variables setting. Possible arguments are joined to the path with a colon ( : ):

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 section. Relative paths are considered relative to the directory where execution is started from.

If a variable file is given as an absolute Windows path, the colon after the drive letter is not considered a separator:

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:

Variables in variable files taken use on the command line 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.

When variable files are taken into use, they are imported as Python modules and all their module level attributes that do not start with an underscore ( _ ) are, by default, 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 section. 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 section below.

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.

Using objects as values

Variables in variable files are not limited to having only strings or other base types as values like Variable sections. Instead, their variables can contain any objects. In the example below, the variable ${MAPPING} contains a Python dictionary and also has two variables created from a custom object implemented in the same file.

Creating variables dynamically

Because variable files are created using a real programming language, they can have dynamic logic for setting variables.

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.

Selecting which variables to include

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:

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.

The __all__ attribute is also, and originally, used by Python to decide which attributes to import when using the syntax from modulename import * .

The third option to select what variables are actually created is using a special get_variables function discussed below.

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 with variable names as keys and variable values as values. Created variables can be used as scalars, lists, and dictionaries exactly like when getting variables directly from a module , 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 example related to getting variables directly from a module .

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 , arguments are specified 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.

Starting from Robot Framework 7.0, arguments to variable files support automatic argument conversion as well as named argument syntax. For example, a variable file with get_variables(first: int = 0, second: str = '') could be imported like this:

It is possible to implement variables files also as a class.

Implementation

Because variable files are always imported using a file system path, the class must have the same name as the module it is located in.

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 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.

The first examples create variables from attributes. It creates variables ${VARIABLE} and @{LIST} from class attributes and ${ANOTHER VARIABLE} from an instance attribute.

The second examples utilizes dynamic approach for getting variables. It creates only one variable ${DYNAMIC VARIABLE} .

Variable files can also be implemented as YAML files. YAML is a data serialization language with a simple and human-friendly syntax that is nevertheless easy for machines to parse. The following example demonstrates a simple YAML file:

YAML variable files can be used exactly like normal variable files from the command line using --variablefile option, in the Settings section using Variables setting, and dynamically using the Import Variables keyword. They are automatically recognized by their extension that must be either .yaml or .yml . If the above YAML file is imported, it will create exactly the same variables as this Variable section:

YAML files used as variable files must always be mappings on 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 section. 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.

Using YAML files with Robot Framework requires PyYAML module to be installed. You can typically install it with pip like pip install pyyaml .

Variable files can also be implemented as JSON files. Similarly as YAML discussed in the previous section, JSON is a data serialization format targeted both for humans and machines. It is based on JavaScript syntax and it is not as human-friendly as YAML, but it still relatively easy to understand and modify. The following example contains exactly the same data as the earlier YAML example:

JSON variable files are automatically recognized by their .json extension and they can be used exactly like YAML variable files. They also have exactly same requirements for structure, encoding, and so on. Unlike YAML, Python supports JSON out-of-the-box so no extra modules need to be installed.

Support for JSON variable files is new in Robot Framework 6.1.

This section describes various structures that can be used to control the test execution flow. These structures are familiar from most programming languages and they allow conditional execution, repeatedly executing a block of keywords and fine-grained error handling. For readability reasons these structures should be used judiciously, and more complex use cases should be preferably implemented in test libraries .

Simple FOR loop

Old for loop syntax, nesting for loops, using several loop variables, for-in-range loop, for-in-enumerate loop, for-in-zip loop, dictionary iteration, removing unnecessary keywords from outputs, repeating single keyword, basic while syntax, limiting while loop iterations, nesting while loops, basic if syntax, else branches, else if branches, nested if structures, other ways to execute keywords conditionally, catching exceptions with except, matching errors using patterns, capturing error message, using else to execute keywords when there are no errors, using finally to execute keywords regardless are there errors or not, other ways to handle errors.

Repeating same actions several times is quite a common need in test automation. With Robot Framework, test libraries can have any kind of loop constructs, and most of the time loops should be implemented in them. Robot Framework also has its own FOR loop syntax, which is useful, for example, when there is a need to repeat keywords from different libraries.

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.

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.

Prior to Robot Framework 3.1, the FOR loop syntax was different than nowadays. The marker to start the loop was :FOR instead of FOR and loop contents needed to be explicitly marked with a backslash instead of using the END marker to end the loop. The first example above would look like this using the old syntax:

The old syntax was deprecated in Robot Framework 3.2 and the support for it was removed altogether in Robot Framework 4.0.

Starting from Robot Framework 4.0, it is possible to use nested FOR loops simply by adding a loop inside another loop:

There can be multiple nesting levels and loops can also be combined with other control structures:

It is possible to iterate over multiple values in one iteration by using multiple loop variables between the FOR and IN markers. There can be any number of loop variables, but the number of values must be evenly dividable by the number of variables. Each iteration consumes as many values as there are 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:

All FOR loops in the previous section iterated over a sequence. That is the most common use case, but sometimes it is convenient to have a loop that is executed a certain number of times. For this purpose Robot Framework has a special FOR index IN RANGE limit loop syntax that 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 that is followed by a loop variable. In this format there can be only one loop variable and it contains the current loop index. After the variable there must be IN RANGE marker (case-sensitive) that is followed by loop limits.

In the simplest case, only the upper limit of the loop is specified. In this case, loop indices 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 indices 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.

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 syntax is just like the regular FOR loop syntax, except that the separator between variables and values is IN ENUMERATE (case-sensitive). Typically they are used so that there is an additional index variable before any other loop-variables. By default the index has a value 0 on the first iteration, 1 on the second, and so on.

For example, the following two test cases do the same thing:

Starting from Robot Framework 4.0, it is possible to specify a custom start index by using start=<index> syntax as the last item of the FOR ... IN ENUMERATE ... header:

The start=<index> syntax must be explicitly used in the FOR header and it cannot itself come from a variable. If the last actual item to enumerate would start with start= , it needs to be escaped like start\= .

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 index variable):

If you only use one loop variable with FOR-IN-ENUMERATE loops, that variable will become a Python tuple containing the index and the iterated value:

FOR-IN-ENUMERATE loops with only one loop variable is a new feature in Robot Framework 3.2.

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 .

This may be easiest to show with an example:

As the example above illustrates, FOR-IN-ZIP loops require their own custom separator IN ZIP (case-sensitive) between loop variables and values. Values used with FOR-IN-ZIP loops must be lists or list-like objects.

Items to iterate over must always be given either as scalar variables like ${items} or as list variables like @{lists} that yield the actual iterated lists. The former approach is more common and it was already demonstrated above. The latter approach works like this:

The number of lists to iterate over is not limited, but it must match the number of loop variables. Alternatively, there can be just one loop variable that then becomes a Python tuple getting items from all lists.

Starting from Robot Framework 6.1, it is possible to configure what to do if lengths of the iterated items differ. By default, the shortest item defines how many iterations there are and values at the end of longer ones are ignored. This can be changed by using the mode option that has three possible values:

  • STRICT : Items must have equal lengths. If not, execution fails. This is the same as using strict=True with Python's zip function.
  • SHORTEST : Items in longer items are ignored. Infinite iterators are supported in this mode as long as one of the items is exhausted. This is the default behavior.
  • LONGEST : The longest item defines how many iterations there are. Missing values in shorter items are filled-in with value specified using the fill option or None if it is not used. This is the same as using Python's zip_longest function except that it has fillvalue argument instead of fill .

All these modes are illustrated by the following examples:

The behavior if list lengths differ will change in the future so that the STRICT mode will be the default. If that is not desired, the SHORTEST mode needs to be used explicitly.

Normal FOR loops and FOR-IN-ENUMERATE loops support iterating over keys and values in dictionaries. This syntax requires at least one of the loop values to be a dictionary variable . It is possible to use multiple dictionary variables and to give additional items in key=value syntax. Items are iterated in the order they are defined and if same key gets multiple values the last value will be used.

Typically it is easiest to use the dictionary iteration syntax so that keys and values get separate variables like in the above examples. With normal FOR loops it is also possible to use just a single variable that will become a tuple containing the key and the value. If only one variable is used with FOR-IN-ENUMERATE loops, it becomes a tuple containing the index, the key and the value. Two variables with FOR-IN-ENUMERATE loops means assigning the index to the first variable and making the second variable a tuple containing the key and the value.

In addition to iterating over names and values in dictionaries, it is possible to iterate over keys and then possibly fetch the value based on it. This syntax requires using dictionaries as list variables :

Iterating over keys and values in dictionaries is a new feature in Robot Framework 3.2. With earlier version it is possible to iterate over dictionary keys like the last example above demonstrates.

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 or flatten unnecessary keywords using --removekeywords and --flattenkeywords command line options.

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.

WHILE loops combine features of FOR loops and IF/ELSE structures . They specify a condition and repeat the loop body as long as the condition remains true. This can be utilised, for example, to repeat a nondeterministic sequence until the desired outcome happens, or in some cases they can be used as an alternative to FOR loops .

WHILE loops are new in Robot Framework 5.0.

The WHILE loop condition is evaluated in Python so that Python builtins like len() are available and modules are imported automatically to support usages like math.pi * math.pow(${radius}, 2) < 10 . Normal variables like ${rc} in the above example are replaced before evaluation, but variables are also available in the evaluation namespace using the special $rc syntax. The latter approach is handy when the string representation of the variable cannot be used in the condition directly. For example, strings require quoting and multiline strings and string themselves containing quotes cause additional problems. See the Evaluating expressions appendix for more information and examples related to the evaluation syntax.

Starting from Robot Framework 6.1, the condition in a WHILE statement can be omitted. This is interpreted as the condition always being true, which may be useful with the limit option described below.

With WHILE loops, there is always a possibility to achieve an infinite loop, either by intention or by mistake. This happens when the loop condition never becomes false. While infinite loops have some utility in application programming, in automation an infinite loop is rarely a desired outcome. If such a loop occurs with Robot Framework, the execution must be forcefully stopped and no log or report can be created. For this reason, WHILE loops in Robot Framework have a default limit of 10 000 iterations. If the limit is exceeded, the loop fails.

The limit can be set with the limit configuration parameter either as a maximum iteration count or as a maximum time for the whole loop. When the limit is an iteration count, it is possible to use just integers like 100 and to add times or x suffix after the value like 100 times . When the limit is a timeout, it is possible to use time strings like 10 s or 1 hour 10 minutes . The limit can also be disabled altogether by using NONE (case-insensitive). All these options are illustrated by the examples below.

Support for using times and x suffixes with iteration counts is new in Robot Framework 7.0.

Keywords in a loop are not forcefully stopped if the limit is exceeded. Instead the loop is exited similarly as if the loop condition would have become false. A major difference is that the loop status will be FAIL in this case.

Starting from Robot Framework 6.1, it is possible to use on_limit parameter to configure the behaviour when the limit is exceeded. It supports two values pass and fail , case insensitively. If the value is pass , the execution will continue normally when the limit is reached and the status of the WHILE loop will be PASS . The value fail works similarly as the default behaviour, e.g. the loop and the test will fail if the limit is exceeded.

By default, the error message raised when the limit is reached is WHILE loop was aborted because it did not finish within the limit of 0.5 seconds. Use the 'limit' argument to increase or remove the limit if needed. . Starting from Robot Framework 6.1, the error message can be changed with the on_limit_message configuration parameter.

on_limit_message configuration parameter is new in Robot Framework 6.1.

WHILE loops can be nested and also combined with other control structures:

WHILE 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 or flatten unnecessary keywords using --removekeywords and --flattenkeywords command line options.

Both FOR and WHILE loop execution can be controlled with BREAK and CONTINUE statements. The former exits the whole loop prematurely and the latter stops executing the current loop iteration and continues to the next one. In practice they have the same semantics as break and continue statements in Python, Java, and many other programming languages.

Both BREAK and CONTINUE are typically used conditionally with IF/ELSE or TRY/EXCEPT structures, and especially the inline IF syntax is often convenient with them. These statements must be used in the loop body, possibly inside the aforementioned control structures, and using them in keyword called in the loop body is invalid.

BREAK and CONTINUE statements are new in Robot Framework 5.0 similarly as WHILE . Earlier versions supported controlling FOR loops using BuiltIn keywords Exit For Loop , Exit For Loop If , Continue For Loop and Continue For Loop If . These keywords still continue to work, but they will be deprecated and removed in the future.

Also the RETURN statement can be used to a exit loop. It only works when loops are used inside a user keyword .

Sometimes there is a need to execute some keywords conditionally. Starting from Robot Framework 4.0 there is a separate IF/ELSE syntax, but there are also other ways to execute keywords conditionally . Notice that if the logic gets complicated, it is typically better to move it into a test library .

Robot Framework's native IF syntax starts with IF (case-sensitive) and ends with END (case-sensitive). The IF marker requires exactly one value that is the condition to evaluate. Keywords to execute if the condition is true are on their own rows between the IF and END markers. Indenting keywords in the IF block is highly recommended but not mandatory.

In the following example keywords Some keyword and Another keyword are executed if ${rc} is greater than zero:

The condition is evaluated in Python so that Python builtins like len() are available and modules are imported automatically to support usages like platform.system() == 'Linux' and math.ceil(${x}) == 1 . Normal variables like ${rc} in the above example are replaced before evaluation, but variables are also available in the evaluation namespace using the special $rc syntax. The latter approach is handy when the string representation of the variable cannot be used in the condition directly. For example, strings require quoting and multiline strings and string themselves containing quotes cause additional problems. For more information and examples related the evaluation syntax see the Evaluating expressions appendix.

Like most other languages supporting conditional execution, Robot Framework IF syntax also supports ELSE branches that are executed if the IF condition is not true.

In this example Some keyword is executed if ${rc} is greater than zero and Another keyword is executed otherwise:

Robot Framework also supports ELSE IF branches that have their own condition that is evaluated if the initial condition is not true. There can be any number of ELSE IF branches and they are gone through in the order they are specified. If one of the ELSE IF conditions is true, the block following it is executed and remaining ELSE IF branches are ignored. An optional ELSE branch can follow ELSE IF branches and it is executed if all conditions are false.

In the following example different keyword is executed depending on is ${rc} positive, negative, zero, or something else like a string or None :

Notice that this example uses the ${rc} variable in the special $rc format to avoid evaluation failures if it is not a number. See the aforementioned Evaluating expressions appendix for more information about this syntax.

Normal IF/ELSE structure is a bit verbose if there is a need to execute only a single statement. An alternative to it is using inline IF syntax where the statement to execute follows the IF marker and condition directly and no END marker is needed. For example, the following two keywords are equivalent:

The inline IF syntax supports also ELSE and ELSE IF branches:

As the latter example above demonstrates, inline IF with several ELSE IF and ELSE branches starts to get hard to understand. Long inline IF structures can be split into multiple lines using the common ... continuation syntax, but using a normal IF/ELSE structure or moving the logic into a test library is probably a better idea. Each inline IF branch can contain only one statement. If more statements are needed, normal IF/ELSE structure needs to be used instead.

If there is a need for an assignment with inline IF , the variable or variables to assign must be before the starting IF . Otherwise the logic is exactly the same as when assigning variables based on keyword return values. If assignment is used and no branch is run, the variable gets value None .

Inline IF syntax is new in Robot Framework 5.0.

IF structures can be nested with each others and with FOR loops . This is illustrated by the following example using advanced features such as FOR-IN-ENUMERATE loop , named-only arguments with user keywords and inline Python evaluation syntax ( ${{len(${items})}} ):

There are also other methods to execute keywords conditionally:

  • The name of the keyword used as a setup or a teardown with suites , tests and keywords 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. The new IF/ELSE syntax explained above is generally recommended, though.
  • 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.

When a keyword fails, Robot Framework's default behavior is to stop the current test and executes its possible teardown . There can, however, be needs to handle these failures during execution as well. Robot Framework 5.0 introduces native TRY/EXCEPT syntax for this purpose, but there also other ways to handle errors .

Robot Framework's TRY/EXCEPT syntax is inspired by Python's exception handling syntax. It has same TRY , EXCEPT , ELSE and FINALLY branches as Python and they also mostly work the same way. A difference is that Python uses lower case try , except , etc. but with Robot Framework all this kind of syntax must use upper case letters. A bigger difference is that with Python exceptions are objects and with Robot Framework you are dealing with error messages as strings.

The basic TRY/EXCEPT syntax can be used to handle failures based on error messages:

In the above example, if Some Keyword passes, the EXCEPT branch is not run and execution continues after the TRY/EXCEPT structure. If the keyword fails with a message Error message (case-sensitive), the EXCEPT branch is executed. If the EXCEPT branch succeeds, execution continues after the TRY/EXCEPT structure. If it fails, the test fails and remaining keywords are not executed. If Some Keyword fails with any other exception, that failure is not handled and the test fails without executing remaining keywords.

There can be more than one EXCEPT branch. In that case they are matched one by one and the first matching branch is executed. One EXCEPT can also have multiple messages to match, and such a branch is executed if any of its messages match. In all these cases messages can be specified using variables in addition to literal strings.

It is also possible to have an EXCEPT without messages, in which case it matches any error. There can be only one such EXCEPT and it must follow possible other EXCEPT branches:

It is not possible to catch exceptions caused by invalid syntax.

By default matching an error using EXCEPT requires an exact match. That can be changed using a configuration option type= as an argument to the except clause. Valid values for the option are GLOB , REGEXP or START (case-insensitive) to make the match a glob pattern match , a regular expression match , or to match only the beginning of the error, respectively. Using value LITERAL has the same effect as the default behavior. If an EXCEPT has multiple messages, this option applies to all of them. The value of the option can be defined with a variable as well.

Remember that the backslash character often used with regular expressions is an escape character in Robot Framework data. It thus needs to be escaped with another backslash when using it in regular expressions.

When matching errors using patterns and when using EXCEPT without any messages to match any error, it is often useful to know the actual error that occurred. Robot Framework supports that by making it possible to capture the error message into a variable by adding AS  ${var} at the end of the EXCEPT statement:

Optional ELSE branches make it possible to execute keywords if there is no error. There can be only one ELSE branch and it is allowed only after one or more EXCEPT branches:

In the above example, if Some Keyword passes, the ELSE branch is executed, and if it fails with message X or Y , the appropriate EXCEPT branch run. In all these cases execution continues after the whole TRY/EXCEPT/ELSE structure. If Some Keyword fail any other way, EXCEPT and ELSE branches are not run and the TRY/EXCEPT/ELSE structure fails.

To handle both the case when there is any error and when there is no error, it is possible to use an EXCEPT without any message in combination with an ELSE :

Optional FINALLY branches make it possible to execute keywords both when there is an error and when there is not. They are thus suitable for cleaning up after a keyword execution somewhat similarly as teardowns . There can be only one FINALLY branch and it must always be last. They can be used in combination with EXCEPT and ELSE branches and having also TRY/FINALLY structure is possible:

  • The BuiltIn keyword Run Keyword And Expect Error executes a named keyword and expects that it fails with a specified error message. It is basically the same as using TRY/EXCEPT with a specified message. The syntax to specify the error message is also identical except that this keyword uses glob pattern matching, not exact match, by default. Using the native TRY/EXCEPT functionality is generally recommended unless there is a need to support older Robot Framework versions that do not support it.
  • The BuiltIn keyword Run Keyword And Ignore Error executes a named keyword and returns its status as string PASS or FAIL along with possible return value or error message. It is basically the same as using TRY/EXCEPT/ELSE so that EXCEPT catches all errors. Using the native syntax is recommended unless old Robot Framework versions need to be supported.
  • The BuiltIn keyword Run Keyword And Return Status executes a named keyword and returns its status as a Boolean true or false. It is a wrapper for the aforementioned Run Keyword And Ignore Error . The native syntax is nowadays recommended instead.
  • Test teardowns and keyword teardowns can be used for cleaning up activities similarly as FINALLY branches.
  • When keywords are implemented in Python based libraries , all Python's error handling features are readily available. This is the recommended approach especially if needed logic gets more complicated.

Keyword scopes

Specifying a keyword explicitly, specifying explicit priority between libraries and resources, test case timeout, user keyword timeout.

Keywords that are used with Robot Framework are either library keywords or user keywords . The former come from standard libraries or external libraries , and the latter are either created in the same file where they are used or then imported from resource files . When many keywords are in use, it is quite common that some of them have the same name, and this section describes how to handle possible conflicts in these situations.

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:

  • Created as a user keyword in the currently executed suite file . These keywords have the highest priority and they are always used, even if there are other keywords with the same name elsewhere.
  • Created in a resource file and imported either directly or indirectly from another resource file. This is the second-highest priority.
  • 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.
  • Created in a standard library. These keywords have the lowest priority.

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 when importing it, 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.

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.

Sometimes keywords may take exceptionally long time 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, the test environment or the 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.

The test case timeout can be set either by using the Test Timeout setting in the Setting section or the [Timeout] setting with individual test cases. Test Timeout defines a default timeout for all the test cases in that suite, whereas [Timeout] applies a timeout to a particular 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 explicit NONE value for this purpose. The timeout is effectively ignored also if its value is zero or negative.

Regardless of where the test timeout is defined, the value given to it contains the duration of the timeout. The duration must be given in Robot Framework's time format , that is, either directly in seconds like 10 or in a format like 1 minute 30 seconds . Timeouts can also be specified as variables making it possible to give them, for example, from the command line.

If there is a timeout and it expires, the keyword that is currently running is stopped and the test case fails. Keywords executed as part of test teardown are not interrupted if a test timeout occurs, though, but the test is nevertheless marked failed. If a keyword in teardown may hang, it can be stopped by using user keyword timeouts .

Timeouts can be set for user keywords using the [Timeout] setting. The syntax is exactly the same as with test case timeout , but user keyword timeouts do not have any default value. If a user keyword timeout is specified using a variable, the value can be given also as a keyword argument.

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.

With earlier Robot Framework versions it was possible to specify a custom error message to use if a timeout expires. This functionality was deprecated in Robot Framework 3.0.1 and removed in Robot Framework 3.2.

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 Process library keywords Start Process and Wait For Process for an example.

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   output files.

Robot Framework test cases are executed from the command line, and the end result is, by default, an output file in XML format and an HTML report and log . After the execution, output files can be combined and otherwise post-processed with the Rebot tool.

Specifying test data to be executed

Using options, short and long options, setting option values, disabling options accepting no values, simple patterns, tag patterns, robot_options and rebot_options environment variables, command line output, generated output files, return codes, errors and warnings during execution, argument file syntax, using argument files, reading argument files from standard input, shell script example, batch file example, python example, using the python debugger (pdb).

Execution is normally started using the robot command created as part of installation . Alternatively it is possible to execute the installed robot module using the selected Python interpreter. This is especially convenient if Robot Framework has been installed under multiple Python versions. Finally, if you know where the installed robot directory exists, it can be executed using Python as well.

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.

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, by default, gets its name 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.

When executing a directory , all files and directories starting with a dot ( . ) or an underscore ( _ ) are ignored and, by default, only files with the .robot extension executed. See the Selecting files to parse section for more details.

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 concatenating 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:

Starting from Robot Framework 6.1, it is also possible to define a test suite initialisation file for the automatically created top-level suite. The path to the init file is given similarly to the test case files:

Robot Framework provides a number of command line options that can be used to control how test cases are executed and what outputs are generated. This section explains the option syntax, and what options actually exist. How they can be used is discussed elsewhere in this chapter.

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 names are case-insensitive and hyphen-insensitive, which facilitates writing option names in an easy-to-read format. For example, --SuiteStatLevel and --suite-stat-level are equivalent to, but easier to read than, --suitestatlevel .

Long options being hyphen-insensitive is new in Robot Framework 6.1.

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.

Many command line options take arguments as simple patterns . These glob-like patterns are matched according to the following rules:

  • * matches any string, even an empty string.
  • ? matches any single character.
  • [abc] matches one character in the bracket.
  • [!abc] matches one character not in the bracket.
  • [a-z] matches one character from the range in the bracket.
  • [!a-z] matches one character not from the range in the bracket.
  • Unlike with glob patterns normally, path separator characters / and \ and the newline character \n are matches by the above wildcards.
  • Unless noted otherwise, pattern matching is case, space, and underscore insensitive.

All matching in above examples is case, space and underscore insensitive. For example, the second example would also match test named example 1 .

If the matched text happens to contain some of the wildcard characters and they need to be matched literally, it is possible to do that by using the [...] syntax. The pattern [*] matches the literal * character, [?] matches ? , and [[] matches [ . Lone [ and ] do not need to be escaped.

Support for brackets like [abc] and [!a-z] is new in Robot Framework 3.1.

Most tag related options accept arguments as tag patterns . They support same wildcards as simple patterns (e.g. examp?? , ex*le ), 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.

The whole pattern matches if all individual patterns match. AND and & are equivalent:

The whole pattern matches if any individual pattern matches:

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:

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 :

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.

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:

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 .

Runner scripts communicate the overall test execution status to the system running them using return codes. When the execution starts successfully and no tests fail, the return code is zero. All possible return codes are explained in the table below.

Possible return codes
RC Explanation
0 All tests passed.
1-249 Returned number of tests failed.
250 250 or more failures.
251 Help or version information printed.
252 Invalid test data or command line options.
253 Test execution stopped by user.
255 Unexpected internal error.

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 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.

Same return codes are also used with Rebot .

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 allow placing all or some command line options and arguments into an external file where they will be read. This avoids the problems with characters that are problematic on the command line. If lot of options or arguments are needed, argument files also prevent the command that is used on the command line growing too long.

Argument files are taken into use with --argumentfile (-A) option along with possible other command line options.

Unlike other long command line options , --argumentfile cannot be given in shortened format like --argumentf .

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:

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:

Both when executing test cases and when post-processing outputs, it is possible to get command line help with the option --help (-h) . These help texts have a short general overview and briefly explain the available command line options.

All runner scripts also support getting the version information with the option --version . This information also contains Python version and the platform type:

Test cases are often executed automatically by a continuous integration system or some other mechanism. In such cases, there is a need to have a script for starting the test execution, and possibly also for post-processing outputs somehow. Similar scripts are also useful when running tests manually, especially if a large number of command line options are needed or setting up the test environment is complicated.

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 this example, the same web tests in the login directory are executed with different browsers and the results combined afterwards using Rebot . The script also accepts command line options itself and simply forwards them to the robot command using the handy $* variable:

Implementing the above shell script example using batch files is not very complicated either. Notice that arguments to batch files can be forwarded to executed commands using %* :

Prior to Robot Framework 3.1 robot and rebot commands were implemented as batch files on Windows and using them in another batch file required prefixing the whole command with call .

When start-up scripts gets more complicated, implementing them using shell scripts or batch files is not that convenient. This is especially true if both variants are needed and same logic needs to be implemented twice. In such situations it is often better to switch to Python. It is possible to execute Robot Framework from Python using the subprocess module , but often using Robot Framework's own programmatic API is more convenient. The easiest APIs to use are robot.run_cli and robot.rebot_cli that accept same command line arguments than the robot and rebot commands.

The following example implements the same logic as the earlier shell script and batch file examples. In Python arguments to the script itself are available in sys.argv :

exit=False is needed because by default run_cli exits to system with the correct return code . rebot_cli does that too, but in the above example that is fine.

On UNIX-like operating systems it is possible to make *.robot files executable by giving them execution permission and adding a shebang like in this example:

If the above content would be in a file example.robot and that file would be executable, it could be executed from the command line like below. Starting from Robot Framework 3.2, individually executed files can have any extension, or no extension at all, so the same would work also if the file would be named just example .

This trick does not work when executing a directory but can be handy when executing a single file. It is probably more often useful when automating tasks than when automating tests.

A test case can fail because the system under test does not work correctly, in which case the test has found a bug, or because the test itself is buggy. The error message explaining the failure is shown on the command line output and in the report file , and sometimes the error message alone is enough to pinpoint the problem. More often that not, however, log files are needed because they have also other log messages and they show which keyword actually failed.

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.

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:

from within a python library or alternatively:

can be used directly in a test case.

This section describes how the test suite structure created from the parsed test data is executed, how test status is determined, and how to continue executing a test case if there are failures, and how to stop the whole test execution gracefully.

Executed suites and tests

Setups and teardowns, execution order, migrating from criticality to skip, suite status, execution continues on teardowns automatically, all top-level keywords are executed when tests have templates, special failures from keywords, run keyword and continue on failure keyword, enabling continue-on-failure using tags, disabling continue-on-failure using tags, builtin keywords, pressing ctrl-c, using signals, using keywords, stopping when first test case fails, stopping on parsing or execution error, handling teardowns.

Test cases are always executed within a test suite. A test suite created from a suite 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.

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.

User keyword setup

User keyword setup is executed before the keyword body. If the setup fails, the body is not executed. There is not much difference between the keyword setup and the first keyword in the body.

User keyword teardown

User keyword teardown is run after the keyword is executed otherwise, regardless the status. User keyword teardowns are executed fully even if some of their keywords would fail.

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.

This section explains how tests can get PASS , FAIL or SKIP status and how the suite status is determined based on test statuses.

The SKIP status is new in Robot Framework 4.0.

A test gets the PASS status if it is executed and none of the keywords it contains fails.

Prematurely passing tests

Normally all keywords are executed, but it is also possible to use BuiltIn keywords Pass Execution and Pass Execution If to stop execution with the PASS status and not run 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 skip the test.

The most common reason for a test to get the FAIL status is that one of the keywords it contains fails. The keyword itself can fail by raising an exception or the keyword can be called incorrectly. Other reasons for failures include syntax errors and the test being empty.

If a suite setup fails, tests in that suite are marked failed without running them. If a suite teardown fails, tests are marked failed retroactively.

Starting from Robot Framework 4.0, tests can get also SKIP status in addition to PASS and FAIL. There are many different ways to get this status.

Skipping before execution

The command line option --skip can be used to skip specified tests without running them at all. It works based on tags and supports tag patterns like examp?? and tagANDanother . If it is used multiple times, all tests matching any of specified tags or tag patterns are skipped:

Starting from Robot Framework 5.0, a test case can also be skipped by tagging the test with the reserved tag robot:skip :

The difference between --skip and --exclude is that with the latter tests are omitted from the execution altogether and they will not be shown in logs and reports. With the former they are included, but not actually executed, and they will be visible in logs and reports.

Skipping dynamically during execution

Tests can get the skip status during execution in various ways:

  • Using the BuiltIn keyword Skip anywhere in the test case, including setup or teardown. Using Skip keyword has two effects: the test gets the SKIP status and rest of the test is not executed. However, if the test has a teardown, it will be run.
  • Using the BuiltIn keyword Skip If which takes a condition and skips the test if the condition is true.
  • Library keywords may also trigger skip behavior by using a special exceptions. This is explained the Skipping tests section in the Creating test libraries chapter.
  • If suite setup is skipped using any of the above means, all tests in the suite are skipped without executing them.
  • If suite teardown is skipped, all tests will be marked skipped retroactively.

Automatically skipping failed tests

The command line option --skiponfailure can be used to automatically mark failed tests skipped. It works based on tags and supports tag patterns like the --skip option discussed above:

Starting from RF 5.0, the reserved tag robot:skip-on-failure can alternatively be used to achieve the same effect as above:

The motivation for this functionality is allowing execution of tests that are not yet ready or that are testing a functionality that is not yet ready. Instead of such tests failing, they will be marked skipped and their tags can be used to separate them from possible other skipped tests.

Earlier Robot Framework versions supported criticality concept that allowed marking tests critical or non-critical. By default all tests were critical, but the --critical and --noncritical options could be used to configure that. The difference between critical and non-critical tests was that non-critical tests were not included when determining the final status for an executed test suite or for the whole test run. In practice the test status was two dimensional having PASS and FAIL in one axis and criticality on the other.

Non-critical failed tests were in many ways similar to the current skipped tests. Because these features are similar and having both SKIP and criticality would have created strange test statuses like non-critical SKIP, the criticality concept was removed in Robot Framework 4.0 when the SKIP status was introduced. The problems with criticality are explained in more detail in the issue that proposed removing it .

The main use case for the criticality concept was being able to run tests that are not yet ready or that are testing a functionality that is not yet ready. This use case is nowadays covered by the skip-on-failure functionality discussed in the previous section.

To ease migrating from criticality to skipping, the old --noncritical option worked as an alias for the new --skiponfailure in Robot Framework 4.0 and also the old --critical option was preserved. Both old options were deprecated and they were removed in Robot Framework 5.0.

Suite status is determined solely based on statuses of the tests it contains:

  • If any test has failed, suite status is FAIL.
  • If there are no failures but at least one test has passed, suite status is PASS.
  • If all tests have been skipped or the are no tests at all, suite status is SKIP.

Normally test cases are stopped immediately when any of their keywords fail. This behavior shortens test execution time and prevents subsequent keywords hanging or otherwise causing problems if the system under test is in unstable state. This has a drawback that often subsequent keywords would give more information about the state of the system, though, and in some cases those subsequent keywords would actually take care of the needed cleanup activities. Hence Robot Framework offers several features to continue even if there are failures.

To make it sure that all the cleanup activities are taken care of, the continue-on-failure mode is automatically enabled in suite, test and keyword teardowns . In practice this means that in teardowns all the keywords in all levels are always executed.

If this behavior is not desired, the special robot:stop-on-failure and robot:recursive-stop-on-failure tags can be used to disable it .

When using test templates , all the top-level keywords are executed to make it sure that all the different combinations are covered. In this usage continuing is limited to the top-level keywords, and inside them the execution ends normally if there are non-continuable failures.

Library keywords report failures using exceptions, and it is possible to use special exceptions to tell Robot Framework that execution can continue regardless the failure. How these exceptions can be created is explained in the Continuable failures section in the Creating test libraries section.

When a test ends and there have been continuable failures, 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 a continuable failure. 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 discussed above.

All keywords executed as part of test cases or user keywords which are tagged with the robot:continue-on-failure tag are considered continuable by default. For example, the following two tests behave identically:

These tags also affect the continue-on-failure mode with different control structures . For example, the below test case will execute the Do Something keyword ten times regardless does it succeed or not:

Setting robot:continue-on-failure within a test case or a user keyword will not propagate the continue-on-failure behavior into user keywords they call. If such recursive behavior is needed, the robot:recursive-continue-on-failure tag can be used. For example, all keywords in the following example are executed:

Setting robot:continue-on-failure or robot:recursive-continue-on-failure in a test case does NOT alter the behaviour of a failure in the keyword(s) executed as part of the [Setup] : The test case is marked as failed and no test case keywords are executed.

The robot:continue-on-failure and robot:recursive-continue-on-failure tags are new in Robot Framework 4.1. They do not work properly with WHILE loops prior to Robot Framework 6.0.

Special tags robot:stop-on-failure and robot:recursive-stop-on-failure can be used to disable the continue-on-failure mode if needed. They work when continue-on-failure has been enabled using tags and also with teardowns and templates :

The robot:stop-on-failure tag affects only test cases and user keywords where it is used and does not propagate to user keywords they call nor to their own teardowns. If recursive behavior affecting all called user keywords and teardowns is desired, the robot:recursive-stop-on-failure tag can be used instead. If there is a need, its effect can again be disabled in lower level keywords by using robot:continue-on-failure or robot:recursive-continue-on-failure tags.

The robot:stop-on-failure and robot:recursive-stop-on-failure tags do not alter the behavior of continuable failures caused by library keywords or by Run Keyword And Continue On Failure . For example, both keywords in this example are run even though robot:stop-on-failure is used:

If robot:recursive-stop-on-failure and robot:continue-on-failure are used together in the same test or keyword, execution is stopped in called keywords if there are failures, but continues in the test or keyword using these tags. If robot:recursive-continue-on-failure and robot:stop-on-failure are used together in the same test or keyword, execution is continued in called keywords if there are failures, but stopped in the test or keyword using these tags.

The robot:stop-on-failure and robot:recursive-stop-on-failure tags are new in Robot Framework 6.0.

Using recursive and non-recursive tags together in same test or keyword is new in Robot Framework 7.0.

Robot Framework 5.0 introduced native TRY/EXCEPT syntax that can be used for handling failures:

For more details see the separate TRY/EXCEPT syntax section.

There are several BuiltIn keywords that can be used to execute other keywords so that execution can continue after possible failures:

  • Run Keyword And Expect Error executes a keyword and expects it to fail with the specified error message. The aforementioned TRY/EXCEPT syntax is nowadays generally recommended instead.
  • Run Keyword And Ignore Error executes a keyword and silences possible error. It returns the status along with possible keyword return value or error message. The TRY/EXCEPT syntax generally works better in this case as well.
  • Run Keyword And Warn On Failure is a wrapper for Run Keyword And Ignore Error that automatically logs a warning if the executed keyword fails.
  • Run Keyword And Return Status executes a keyword and returns Boolean True or False depending on did it pass or fail.

Sometimes there is a need to stop the test execution before all the tests have finished, but so that logs and reports are created. Different ways how to accomplish this are explained below. In all these cases the remaining test cases are marked failed.

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.

Prior to Robot Framework 3.1, the special tag was named robot-exit .

The execution is stopped when Ctrl-C is pressed in the console where the tests are running. The execution is stopped immediately, but reports and logs are still generated.

If Ctrl-C is pressed again, the execution ends immediately and reports and logs are not created.

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.

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 test fails. The remaining tests are marked as failed without actually executing them.

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.

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.

Robot Framework can be used also for other automation purposes than test automation, and starting from Robot Framework 3.1 it is possible to explicitly create and execute tasks. For most parts task execution and test execution work the same way, and this section explains the differences.

When Robot Framework is used execute a file and it notices that the file has tasks, not tests, it automatically sets itself into the generic automation mode. This mode does not change the actual execution at all, but when logs and reports are created, they use term task , not test . They have, for example, headers like Task Log and Task Statistics instead of Test Log and Test Statistics .

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.

All normal command line options can be used when executing tasks. If there is a need to select only certain tasks for execution , --task can be used instead of --test . Additionally the aforementioned --rpa can be used to control the execution mode.

XML output files that are generated during the test execution can be post-processed afterwards by the Rebot tool, which is an integral part of Robot Framework. It is used automatically when test reports and logs are generated during the test execution, and using it separately allows creating custom reports and logs as well as combining and merging results.

Specifying options and arguments

Return codes with rebot, controlling execution mode, merging re-executed tests, merging suites executed in pieces.

The most common way to use Rebot is using the rebot command. Alternatively it is possible to execute the installed robot.rebot module or the robot/rebot.py file using the selected Python interpreter.

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.

You can use Rebot for creating the same reports and logs that are created automatically during the test execution. Of course, it is not sensible to create the exactly same files, but, for example, having one report with all test cases and another with only some subset of tests can be useful:

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.

Rebot does not create XML output files by default, but it is possible to create them by using the --output (-o) option. Log and report are created by default, but they can be disabled by using value NONE (case-insensitive) if they are not needed:

An important feature in Rebot is its ability to combine outputs from different test execution rounds. This capability allows, for example, running the same test cases on different environments and generating an overall report from all outputs. Combining outputs is extremely easy, all that needs to be done is giving several output files as arguments:

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:

If same tests are re-executed or a single test suite executed in pieces, combining results like discussed above creates an unnecessary top-level test suite. In these cases it is typically better to merge results instead. Merging is done by using --merge (-R) option which changes the way how Rebot combines two or more output files. This option itself takes no arguments and all other command line options can be used with it normally:

When suites are merged, documentation, suite setup and suite teardown are got from the last merged suite. Suite metadata from all merged suites is preserved so that values in latter suites have precedence.

How merging tests works is explained in the following sections discussing the two main merge use cases.

Getting suite documentation and metadata from merged suites is new in Robot Framework 6.0.

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. An exception to this rule is that skipped tests in latter runs are ignored and original tests preserved.

This 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.

Ignoring skipped tests in latter runs is new in Robot Framework 4.1.

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:

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.

Rebot can create and process output files also in the JSON format. Creating JSON output files is done using the normal --output option so that the specified file has a .json extension:

When reading output files, JSON files are automatically recognized by the extension:

When combining or merging results, it is possible to mix JSON and XML files:

The JSON output file structure is documented in the result.json schema file .

Support for JSON output files is new in Robot Framework 7.0.

This section explains different command line options that can be used for configuring the test execution or post-processing outputs . Options related to generated output files are discussed in the next section.

Executing individual files

Included and excluded files, selecting files by name or path, selecting files by extension, using custom parsers, by test names, by suite names, by tag names, re-executing failed test cases, re-executing failed test suites, when no tests match selection, setting suite name, setting suite documentation, setting free suite metadata, setting test tags, locations automatically in module search path, using --pythonpath option, configuring sys.path programmatically, example: select every xth test, example: exclude tests by name, example: disable setups and teardowns, console output type, console width, console colors, console links, console markers.

When executing individual files, Robot Framework tries to parse and run them regardless the name or the file extension. What parser to use depends on the extension:

  • .robot files and files that are not recognized are parsed using the normal Robot Framework parser .
  • .rst and .rest files are parsed using the reStructuredText parser .
  • .rbt and .json files are parsed using the JSON parser .
  • Files supported by custom parsers are parsed by a matching parser.

When executing a directory , files and directories are parsed using the following rules:

  • All files and directories starting with a dot ( . ) or an underscore ( _ ) are ignored.
  • .robot files are parsed using the normal Robot Framework parser .
  • .robot.rst files are parsed using the reStructuredText parser .
  • .rbt files are parsed using the JSON parser .
  • Other files are ignored unless parsing them has been enabled by using the --parseinclude or --extension options discussed in the subsequent sections.

When executing a directory, it is possible to parse only certain files based on their name or path by using the --parseinclude (-I) option. This option has slightly different semantics depending on the value it is used with:

  • If the value is just a file name like example.robot , files matching the name in all directories will be parsed.
  • To match only a certain file in a certain directory, files can be given as relative or absolute paths like path/to/tests.robot .
  • If the value is a path to a directory, all files inside that directory are parsed, recursively.

Values used with --parseinclude are case-insensitive and support glob patterns like example_*.robot . There are, however, two small differences compared to how patterns typically work with Robot Framework:

  • * matches only a single path segment. For example, path/*/tests.robot matches path/to/tests.robot but not path/to/nested/tests.robot .
  • ** can be used to enable recursive matching. For example, path/**/tests.robot matches both path/to/tests.robot and path/to/nested/tests.robot .

If the pattern contains an extension, files with that extension are parsed even if they by default would not be . What parser to use depends on the used extension:

  • .json files are parsed using the JSON parser .
  • Other files are parsed using the normal Robot Framework parser .

Notice that when you use a pattern like *.robot and there exists a file that matches the pattern in the execution directory, the shell may resolve the pattern before Robot Framework is called and the value passed to it is the file name, not the original pattern. In such cases you need to quote or escape the pattern like '*.robot' or \*.robot .

--parseinclude is new in Robot Framework 6.1.

In addition to using the --parseinclude option discussed in the previous section, it is also possible to enable parsing files that are not parsed by default by using the --extension (-F) option. Matching extensions is case insensitive and the leading dot can be omitted. If there is a need to parse more than one kind of files, it is possible to use a colon : to separate extensions:

The above is equivalent to the following --parseinclude usage:

Because the --parseinclude option is more powerful and covers all same use cases as the --extension option, the latter is likely to be deprecated in the future. Users are recommended to use --parseinclude already now.

External parsers can parse files that Robot Framework does not recognize otherwise. For more information about creating and using such parsers see the Parser interface section.

Robot Framework offers several command line options for selecting which test cases to execute. The same options work also when executing tasks and when post-processing outputs with Rebot .

The easiest way to select only some tests to be run is using the --test (-t) option. As the name implies, it can be used for selecting tests by their names. Given names are case, space and underscore insensitive and they also support simple patterns . The option can be used multiple times to match multiple tests:

To pinpoint a test more precisely, it is possible to prefix the test name with a suite name:

Notice that when the given name includes a suite name, it must match the whole suite name starting from the root suite. Using a wildcard as in the last example above allows matching tests with a parent suite anywhere.

Using the --test option is convenient when only a few tests needs to be selected. A common use case is running just the test that is currently being worked on. If a bigger number of tests needs to be selected, it is typically easier to select them by suite names or by tag names .

When executing tasks , it is possible to use the --task option as an alias for --test .

Tests can be selected also by suite names with the --suite (-s) option that selects all tests in matching suites. Similarly as with --test , given names are case, space and underscore insensitive and support simple patterns . To pinpoint a suite more precisely, it is possible to prefix the name with the parent suite name:

If the name contains a parent suite name, it must match the whole suite name the same way as with --test . Using a wildcard as in the last example above allows matching suites with a parent suite anywhere.

Prior to Robot Framework 7.0, --suite with a parent suite did not need to match the whole suite name. For example, parent.child would match suite child with parent parent anywhere. The name must be prefixed with a wildcard if this behavior is desired nowadays.

If both --suite and --test options are used, only the specified tests in specified suites are selected:

Using the --suite option is more or less the same as executing the appropriate suite file or directory directly. The main difference is that if a file or directory is run directly, possible suite setups and teardowns on higher level are not executed:

Prior to Robot Framework 6.1, files not matching the --suite option were not parsed at all for performance reasons. This optimization was not possible anymore after suites got a new Name setting that can override the default suite name that is got from the file or directory name. New --parseinclude option has been added to explicitly select which files are parsed if this kind of parsing optimization is needed.

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:

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:

Starting from RF 5.0, it is also possible to use the reserved tag robot:exclude to achieve the same effect as with using the --exclude option:

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 ).

Options --include and --exclude can be used in combination with --suite and --test discussed in the previous section. In that case tests that are selected must match all selection criteria:

In Robot Framework 7.0 --include and --test were cumulative and selected tests needed to match only either of these options. That behavior caused backwards incompatibility problems and it was changed back to the original already in Robot Framework 7.0.1.

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 using the --test option. It is possible to further fine-tune the list of selected tests by using --test , --suite , --include and --exclude options.

It is an error if the output contains no failed tests, but this behavior can be changed by using the --runemptysuite option discussed below . Using an output not originating from executing the same tests that are run now causes undefined results. Using a special value NONE as the output is same as not specifying this option at all.

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.

By default when no tests match the selection criteria test execution fails with an error like:

Because no outputs are generated, this behavior can be problematic if tests are executed and results processed automatically. Luckily a command line option --RunEmptySuite (case-insensitive) 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.

Using --RunEmptySuite with --ReRunFailed or --ReRunFailedSuites requires Robot Framework 5.0.1 or newer.

When Robot Framework parses test data, 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) :

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 and must be quoted if it contains spaces.

If the given documentation is a relative or absolute path pointing to an existing file, the actual documentation will be read from that file. This is especially convenient if the externally specified documentation is long or contains multiple lines.

Reading documentation from an external file is new in Robot Framework 4.1.

Prior to Robot Framework 3.1, underscores in documentation were converted to spaces same way as with the --name option.

Free 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 and the whole argument must be quoted if it contains spaces. This option may be used several times to set multiple metadata values.

If the given value is a relative or absolute path pointing to an existing file, the actual value will be read from that file. This is especially convenient if the value is long or contains multiple lines. If the value should be a path to an existing file, not read from that file, the value must be separated with a space from the name: part.

Reading metadata value from an external file is new in Robot Framework 4.1.

Prior to Robot Framework 3.1, underscores in the value were converted to spaces same way as with the --name option.

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.

When Robot Framework imports a test library , listener , or some other Python based extension, it uses the Python interpreter to import the module containing the extension from the system. The list of locations where modules are looked for is called the module search path , and its contents can be configured using different approaches explained in this section.

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 reads additional locations to be added to the module search path from PYTHONPATH environment variables. 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 .

Robot Framework has a separate command line option --pythonpath (-P) for adding locations to the module search path.

Multiple locations can be given by separating them with a colon ( : ) or a semicolon ( ; ) or by using this option multiple times. If the value contains both colons and semicolons, it is split from semicolons. Paths can also be glob patterns matching multiple paths, but they typically need to be escaped when used on the console.

Both colon and semicolon work regardless the operating system. Using semicolon is new in Robot Framework 5.0.

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.

Variables can be set from the command line either individually using the --variable (-v) option or through variable files with the --variablefile (-V) option. Variables and variable files are explained in separate chapters, but the following examples illustrate how to use these options:

Robot Framework supports so called dry run mode where the tests are run normally otherwise, but the keywords coming from the test libraries are not executed at all. The dry run mode can be used to validate the test data; if the dry run passes, the data should be syntactically correct. This mode is triggered using option --dryrun .

The dry run execution may fail for following reasons:

Using keywords that are not found. Using keywords with wrong number of arguments. Using user keywords that have invalid syntax.

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.

The dry run mode does not validate variables.

The test execution order can be randomized using option --randomize <what>[:<seed>] , where <what> is one of the following:

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.

If the provided built-in features to modify test data before execution are not enough, Robot Framework makes it possible to do custom modifications programmatically. This is accomplished by creating a so called pre-run modifier and activating it using the --prerunmodifier option.

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 used first is considered to be the actual separator. Starting from Robot Framework 4.0, arguments also support the named argument syntax as well as argument conversion based on type hints and default values the same way as keywords do.

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.

Pre-run modifiers are executed before other configuration affecting the executed test suite and test cases. Most importantly, options related to selecting test cases are processed after modifiers, making it possible to use options like --include also with possible dynamically added tests.

Modifiers are taken into use from the command line exactly the same way as listeners . See the Registering listeners from command line section for more information and examples.

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.

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:

Argument conversion based on type hints like x: int in the above example is new in Robot Framework 4.0 and requires Python 3.

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.

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:

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:

Prior to Robot Framework 4.0 setup and teardown were accessed via the intermediate keywords attribute and, for example, suite setup was disabled like suite.keywords.setup = None .

There are various command line options to control how test execution is reported on the console.

The overall console output type is set with the --console option. It supports the following case-insensitive values:

Separate convenience options --dotted (-.) and --quiet are shortcuts for --console dotted and --console quiet , respectively.

The width of the test execution output in the console can be set using the option --consolewidth (-W) . The default width is 78 characters.

On many UNIX-like machines you can use handy $COLUMNS environment variable like --consolewidth $COLUMNS .

The --consolecolors (-C) option is used to control whether colors should be used in the console output. Colors are implemented using ANSI escape codes with a backup mechanism for older Windows versions that do not support ANSI codes.

This option supports the following case-insensitive values:

Using ANSI codes on Windows by default is new in Robot Framework 7.1.

Result file paths written to the console at the end of the execution are, by default, hyperlinks. This behavior can be controlled with the --consolelinks option that accepts the following case-insensitive values:

The hyperlink support depends also on the console that is used, but nowadays the support is pretty good . The commonly used Windows Console does not support links, though, but the newer Windows Terminal does.

Hyperlink support is new in Robot Framework 7.1.

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:

Listeners can be used to monitor the test execution. When they are taken into use from the command line, they are specified using the --listener command line option. The value can either be a path to a listener or a listener name. See the Listener interface section for more details about importing listeners and using them in general.

Several output files are created when tests are executed, and all of them are somehow related to test results. This section discusses what outputs are created, how to configure where they are created, and how to fine-tune their contents.

Output directory

Output file, legacy output file format, report file, xunit compatible result file, timestamping output files, setting titles, setting background colors, available log levels, setting log level, visible log level, configuring displayed suite statistics, including and excluding tag statistics, generating combined tag statistics, creating links from tag names, adding documentation to tags, removing keywords, flattening keywords, flattening keyword during execution time.

This section explains what different output files can be created and how to configure where they are created. Output files are configured using command line options, which get the path to the output file in question as an argument. A special value NONE (case-insensitive) can be used to disable creating a certain output file.

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 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 .

Generating report and xUnit files as part of test execution does not require processing output files after execution. 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 .

The XML output file structure is documented in the robot.xsd schema file .

Starting from Robot Framework 7.0, Rebot can read and write JSON output files . The plan is to enhance the support for JSON output files in the future so that they could be created already during execution. For more details see issue #3423 .

There were some backwards incompatible changes to the output file format in Robot Framework 7.0. To make it possible to use new Robot Framework versions with external tools that are not yet updated to support the new format, there is a --legacyoutput option that produces output files that are compatible with Robot Framework 6.x and earlier. Robot Framework itself can process output files both in the old and in the new formats.

We hope that external tools are updated soon, but we plan to support this option at least until Robot Framework 8.0. If you encounter tools that are not compatible, please inform the tool developers about changes.

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 .

src/ExecutingTestCases/log_passed.png

An example of beginning of a log file

src/ExecutingTestCases/log_failed.png

An example of a log file with keyword details visible

src/ExecutingTestCases/log_skipped.png

An example of a log file with skipped and passed tests

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 tests pass and bright red if any test fails. Background can also be yellow, which means that all tests were skipped .

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 .

src/ExecutingTestCases/report_passed.png

An example report file of successful test execution

src/ExecutingTestCases/report_failed.png

An example report file of failed 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.

Jenkins also has a separate Robot Framework plugin .

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.

XUnit output files were changed pretty heavily in Robot Framework 5.0. They nowadays contain separate <testsuite> elements for each suite, <testsuite> elements have timestamp attribute, and suite documentation and metadata is stored as <property> elements.

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.

All output files generated by Robot Framework itself 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 output files like output-20080604-163225.xml and mylog-20080604-163225.html :

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.

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 red background if there are failures, green background if there are passed tests and possibly some skipped ones, and a yellow background if all tests are skipped or no tests have been run. 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:

If you specify two colors, the first one will be used instead of the default green (pass) color and the second instead of the default red (fail). 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 two have same semantics as earlier and the last one replaces the default yellow (skip) color.

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, red and yellow colors are specified using hexadecimal values #9e9 , #f66 and #fed84f , 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:

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.

src/ExecutingTestCases/visible_log_level.png

An example log showing the visible log level drop down

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:

In the above example, tests are run using level DEBUG , but the default visible level in the log file is INFO .

Normally the log file is just a single HTML file. When the amount of the test cases increases, the size of the file can grow so large that opening it into a browser is inconvenient or even impossible. Hence, it is possible to use the --splitlog option to split parts of the log into external files that are loaded transparently into the browser when needed.

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.

When copying the log files, you need to copy also all the log-*.js files or some information will be missing.

There are several command line options that can be used to configure and adjust the contents of the Statistics by Tag , Statistics by Suite and Test Details by Tag tables in different output files. All these options work both when executing test cases and when post-processing outputs.

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:

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 :

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:

src/ExecutingTestCases/tagstatcombine.png

Examples of combined tag statistics

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 ( : ):

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:

src/ExecutingTestCases/tagstatlink.png

Examples of links from tag names

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.

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.

Most of the content of output files comes from keywords and their log messages. When creating higher level reports, log files are not necessarily needed at all, and in that case keywords and their messages just take space unnecessarily. Log files themselves can also grow overly large, especially if they contain FOR loops or other constructs that repeat certain keywords multiple times.

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.

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.

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 .

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:

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.

Starting from Robot Framework 6.1, it is possible to enable the keyword flattening during the execution time. This can be done only on an user keyword level by defining the reserved tag robot:flatten as a keyword tag . Using this tag will work similarly as the command line option described in the previous chapter, e.g. all content except for log messages is removed from under the keyword having the tag. One important difference is that in this case, the removed content is not written to the output file at all, and thus cannot be accessed at later time.

Some examples

Keywords that have passed are closed in the log file by default. Thus information they contain is not visible unless you expand them. If certain keywords have important information that should be visible when the log file is opened, you can use the --expandkeywords option to set keywords automatically expanded in log file similar to failed keywords. Expanding supports the following modes:

If you need to expand keywords matching different names or patterns, you can use the --expandkeywords multiple times.

The --expandkeywords option is new in Robot Framework 3.2.

When combining outputs using Rebot, it is possible to set the start and end time of the combined test suite using the options --starttime and --endtime , respectively. This is convenient, because by default, combined suites do not have these values. When both the start and end time are given, the elapsed time is also calculated based on them. Otherwise the elapsed time is got by adding the elapsed times of the child test suites together.

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.

If a test case fails and has a long error message, the message shown in reports is automatically cut from the middle to keep reports easier to read. By default messages longer than 40 lines are cut, but that can be configured by using the --maxerrorlines command line option. The minimum value for this option is 10, and it is also possible to use a special value NONE to show the full message.

Full error messages are always visible in log files as messages of the failed keywords.

The --maxerrorlines option is new in Robot Framework 3.1.

If the provided built-in features to modify results are not enough, Robot Framework makes it possible to do custom modifications programmatically. This is accomplished by creating a model modifier and activating it using the --prerebotmodifier option.

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:

If the above modifier would be in file ExecutionTimeChecker.py , it could be used, for example, like this:

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.

Argument conversion based on type hints like max_seconds: float in the above example is new in Robot Framework 4.0 and requires Python 3.

Robot Framework has its own plain-text system log where it writes information about

Processed and skipped test data files Imported test libraries, resource files and variable files Executed test suites and test cases Created outputs

Normally users never need this information, but it can be useful when investigating problems with test libraries or Robot Framework itself. A system log is not created by default, but it can be enabled by setting the environment variable ROBOT_SYSLOG_FILE so that it contains a path to the selected file.

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.

4   Extending Robot Framework

4.1   creating test libraries, 4.2   remote library interface, 4.3   listener interface, 4.4   parser interface.

Robot Framework's actual testing capabilities are provided by test libraries. There are many existing libraries, some of which are even bundled with the core framework, but there is still often a need to create new ones. This task is not too complicated because, as this chapter illustrates, Robot Framework's library API is simple and straightforward.

Supported programming languages

Different test library apis, library name, providing arguments to libraries, library scope, library version, documentation format, library acting as listener, @library decorator, what methods are considered keywords, limiting public methods becoming keywords, keyword names, keyword tags, keyword arguments, default values to keywords, variable number of arguments ( *varargs ), free keyword arguments ( **kwargs ), keyword-only arguments, positional-only arguments, argument conversion, @keyword decorator, @not_keyword decorator, using custom decorators, embedding arguments into keyword names, asynchronous keywords, reporting keyword status, exceptions provided by robot framework, continuable failures, skipping tests, stopping test execution, logging information, programmatic logging apis, logging during library initialization, returning values, detecting is robot framework running, communication when using threads, documenting libraries, testing libraries, packaging libraries, deprecating keywords, getting keyword names, running keywords, getting keyword arguments, getting keyword argument types, getting keyword tags, getting keyword documentation, getting general library documentation, getting keyword source information, named argument syntax with dynamic libraries, free named arguments with dynamic libraries, named-only arguments with dynamic libraries, getting keyword arguments and documentation, available apis, using builtin library, modifying original source code, using inheritance, using other libraries directly, getting active library instance from robot framework, libraries using dynamic or hybrid api.

Robot Framework itself is written with Python and naturally test libraries extending it can be implemented using the same language. 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 Python 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.

Robot Framework has three different test library APIs.

The simplest approach is having a module or a class with functions/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.
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.

Test libraries can be implemented as Python modules or classes.

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 .

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 .

If the library name is really long, it is recommended to give the library a simpler alias by using AS .

All test libraries implemented as classes can take arguments. These arguments are specified in the Setting section 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 . 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 for the libraries used in the above example:

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:

A new instance is created for every test case. A possible suite setup and suite teardown share yet another instance.

Prior to Robot Framework 3.2 this value was TEST CASE , but nowadays TEST is recommended. Because all unrecognized values are considered same as TEST , both values work with all versions. For the same reason it is possible to also use value TASK if the library is targeted for RPA usage more than testing. TEST is also the default value if the ROBOT_LIBRARY_SCOPE attribute is not set.

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.

Prior to Robot Framework 3.2 this value was TEST SUITE . That value still works, but SUITE is recommended with libraries targeting Robot Framework 3.2 and newer.

If a library is imported multiple times with different arguments , a new instance is created every time regardless the scope.

When the SUITE or GLOBAL scopes are used with 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.

Example library using the SUITE scope:

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 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.

An example module using __version__ :

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 example that uses reStructuredText format. See Documenting libraries section and Libdoc chapter for more information about documenting test libraries in general.

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 Libraries as listeners section.

An easy way to configure libraries implemented as classes is using the robot.api.deco.library class decorator. It allows configuring library's scope , version , custom argument converters , documentation format and listener with optional arguments scope , version , converter , doc_format and listener , respectively. When these arguments are used, they set the matching ROBOT_LIBRARY_SCOPE , ROBOT_LIBRARY_VERSION , ROBOT_LIBRARY_CONVERTERS , ROBOT_LIBRARY_DOC_FORMAT and ROBOT_LIBRARY_LISTENER attributes automatically:

The @library decorator also disables the automatic keyword discovery by setting the ROBOT_AUTO_KEYWORDS argument to False by default. This means that it is mandatory to decorate methods with the @keyword decorator to expose them as keywords. If only that behavior is desired and no further configuration is needed, the decorator can also be used without parenthesis like:

If needed, the automatic keyword discovery can be enabled by using the auto_keywords argument:

The @library decorator only sets class attributes ROBOT_LIBRARY_SCOPE , ROBOT_LIBRARY_VERSION , ROBOT_LIBRARY_CONVERTERS , ROBOT_LIBRARY_DOC_FORMAT and ROBOT_LIBRARY_LISTENER if the respective arguments scope , version , converters , doc_format and listener are used. The ROBOT_AUTO_KEYWORDS attribute is set always. When attributes are set, they override possible existing class attributes.

The @library decorator is new in Robot Framework 3.2 and converters argument is new in Robot Framework 5.0.

When the static library API is used, Robot Framework uses introspection to find out what keywords the library class or module implements. By default it excludes methods and functions starting with an underscore. All the methods and functions that are not ignored are considered keywords. For example, the library below implements a single keyword My Keyword .

Automatically considering all public methods and functions keywords typically works well, but there are cases where it is not desired. There are also situations where keywords are created when not expected. For example, when implementing a library as class, it can be a surprise that also methods in possible base classes are considered keywords. When implementing a library as a module, functions imported into the module namespace becoming keywords is probably even a bigger surprise.

This section explains how to prevent methods and functions becoming keywords.

Class based libraries

When a library is implemented as a class, it is possible to tell Robot Framework not to automatically expose methods as keywords by setting the ROBOT_AUTO_KEYWORDS attribute to the class with a false value:

When the ROBOT_AUTO_KEYWORDS attribute is set like this, only methods that have explicitly been decorated with the @keyword decorator or otherwise have the robot_name attribute become keywords. The @keyword decorator can also be used for setting a custom name , tags and argument types to the keyword.

Although the ROBOT_AUTO_KEYWORDS attribute can be set to the class explicitly, it is more convenient to use the @library decorator that sets it to False by default:

Both limiting what methods become keywords using the ROBOT_AUTO_KEYWORDS attribute and the @library decorator are new in Robot Framework 3.2.

Another way to explicitly specify what keywords a library implements is using the dynamic or the hybrid library API.

Module based libraries

When implementing a library as a module, all functions in the module namespace become keywords. This is true also with imported functions, and that can cause nasty surprises. For example, if the module below would be used as a library, it would contain a keyword Example Keyword , as expected, but also a keyword Current Thread .

A simple way to avoid imported functions becoming keywords is to only import modules (e.g. import threading ) and to 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 one keyword Example Keyword :

If the library is big, maintaining the __all__ attribute when keywords are added, removed or renamed can be a somewhat big task. Another way to explicitly mark what functions are keywords is using the ROBOT_AUTO_KEYWORDS attribute similarly as it can be used with class based libraries . When this attribute is set to a false value, only functions explicitly decorated with the @keyword decorator become keywords. For example, also this library implements only one keyword Example Keyword :

Limiting what functions become keywords using ROBOT_AUTO_KEYWORDS is a new feature in Robot Framework 3.2.

Using @not_keyword decorator

Functions in modules and methods in classes can be explicitly marked as "not keywords" by using the @not_keyword decorator. When a library is implemented as a module, this decorator can also be used to avoid imported functions becoming keywords.

Using the @not_keyword decorator is pretty much the opposite way to avoid functions or methods becoming keywords compared to disabling the automatic keyword discovery with the @library decorator or by setting the ROBOT_AUTO_KEYWORDS to a false value. Which one to use depends on the context.

The @not_keyword decorator is new in Robot Framework 3.2.

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.

Example library implemented as a module in the MyLibrary.py file:

The example below illustrates how the example library above can be used. If you want to try this yourself, make sure that the library is in the module search path .

Setting custom name

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:

Instead of explicitly setting the robot_name attribute like in the above example, it is typically easiest to use the @keyword decorator :

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. 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 the embedded arguments syntax.

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. Similarly as when setting custom name , it is easiest to set this attribute by using the @keyword decorator :

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:

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, the method 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.

Example keywords taking different numbers of arguments:

It is often useful that some of the arguments that a keyword uses have default values.

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:

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.

Robot Framework supports also keywords that take any number of arguments.

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:

Robot Framework supports Python's **kwargs syntax . 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.

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:

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:

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.

Starting from Robot Framework 3.1, it is possible to use named-only arguments with different keywords. This support is provided by Python's keyword-only arguments . Keyword-only arguments are specified after possible *varargs or after a dedicated * marker when *varargs are not needed. Possible **kwargs are specified after keyword-only arguments.

Python supports so called positional-only arguments that make it possible to specify that an argument can only be given as a positional argument , not as a named argument like name=value . Positional-only arguments are specified before normal arguments and a special / marker must be used after them:

The above keyword could be used like this:

If a positional-only argument is used with a value that contains an equal sign like example=usage , it is not considered to mean named argument syntax even if the part before the = would match the argument name. This rule only applies if the positional-only argument is used in its correct position without other arguments using the name argument syntax before it, though.

Positional-only arguments are fully supported starting from Robot Framework 4.0. Using them as positional arguments works also with earlier versions, but using them as named arguments causes an error on Python side.

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 function annotations or the @keyword decorator . In these cases Robot Framework converts arguments automatically.
  • Automatic conversion is also done based on keyword default values .
  • Libraries can register custom argument converters .

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.

Prior to Robot Framework 4.0, automatic conversion was done only if the given argument was a string. Nowadays it is done regardless the argument type.

Manual argument conversion

If no type information is specified to Robot Framework, all arguments not passed as variables are given to keywords as Unicode strings. This includes cases like this:

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:

Keywords can also use Robot Framework's argument conversion functionality via the robot.api.TypeInfo class and its convert method. This can be useful if the needed conversion logic is more complicated or the are needs for better error reporting than what simply using, for example, int() provides.

It is generally recommended to specify types using type hints or otherwise and let Robot Framework handle argument conversion automatically. Manual argument conversion should only be needed in special cases.

robot.api.TypeInfo is new in Robot Framework 7.0.

Specifying argument types using function annotations

Starting from Robot Framework 3.1, arguments passed to keywords are automatically converted if argument type information is available and the type is recognized. The most natural way to specify types is using Python 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 .

Keywords can also have a return type annotation specified using the -> notation at the end of the signature like def example() -> int: . This information is not used for anything during execution, but starting from Robot Framework 7.0 it is shown by Libdoc for documentation purposes.

Specifying argument types using @keyword decorator

An alternative way to specify explicit argument types is using the @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:

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 to mark that a certain argument does not have type information and arguments at the end can be omitted altogether. For example, both of these keywords specify the type only for the second argument:

Starting from Robot Framework 7.0, it is possible to specify the keyword return type by using key 'return' with an appropriate type in the type dictionary. This information is not used for anything during execution, but it is shown by Libdoc for documentation purposes.

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 an argument has an explicit type and a default value, conversion is first attempted based on the explicit type. If that fails, then conversion is attempted based on the default value. In this special case conversion based on the default value is strict and a conversion failure causes an error.

If argument conversion based on default values is not desired, the whole argument conversion can be disabled with the @keyword decorator like @keyword(types=None) .

Prior to Robot Framework 4.0 conversion was done based on the default value only if the argument did not have an explict type.

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 regardless of the type of the given argument. If the argument type is incompatible with the expected type, conversion fails.
  • Conversion failures cause an error if the type has been specified explicitly. If the type is got based on a default value, the given argument is used as-is.

If an argument has both a type hint and a default value, conversion is first attempted based on the type hint and then, if that fails, based on the default value type. This behavior is likely to change in the future so that conversion based on the default value is done only if the argument does not have a type hint. That will change conversion behavior in cases like arg: list = None where None conversion will not be attempted anymore. Library creators are strongly recommended to specify the default value type explicitly like arg: list | None = None already now.

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 ). Also types in in the typing module that map to the supported concrete types or ABCs (e.g. List ) are supported. In all these cases the argument is converted to the concrete type.

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.

The Accepts column specifies which given argument types are converted. If the given argument already has the expected type, no conversion is done. Other types cause conversion failures.

Supported argument conversions
Type ABC Aliases Accepts Explanation Examples
  boolean , , ,

Strings , , and are converted to , the empty string as well as , , and are converted to , and the string is converted to . Other strings and other accepted values are passed as-is, allowing keywords to handle them specially if needed. All string comparisons are case-insensitive.

True and false strings can be . See the appendix for supported translations.

(converted to ) (converted to ) (used as-is)
integer, long ,

Conversion is done using the built-in function. Floats are accepted only if they can be represented as integers exactly. For example, is accepted and is not. If converting a string to an integer fails and the type is got implicitly based on a default value, conversion to float is attempted as well.

Starting from Robot Framework 4.1, it is possible to use hexadecimal, octal and binary numbers by prefixing values with , and , respectively.

Starting from Robot Framework 4.1, spaces and underscores can be used as visual separators for digit grouping purposes.

Starting from Robot Framework 7.0, strings representing floats are accepted as long as their decimal part is zero. This includes using the scientific notation like .

double ,

Conversion is done using the built-in.

Starting from Robot Framework 4.1, spaces and underscores can be used as visual separators for digit grouping purposes.

    , ,

Conversion is done using the class. is recommended over when decimal numbers need to be represented exactly.

Starting from Robot Framework 4.1, spaces and underscores can be used as visual separators for digit grouping purposes.

  string, unicode Any

All arguments are converted to Unicode strings.

New in Robot Framework 4.0.

 
    , Strings are converted to bytes so that each Unicode code point below 256 is directly mapped to a matching byte. Higher code points are not allowed. (converted to ) (the null byte)
    , Same conversion as with , but the result is a .  
    , ,

Strings are expected to be timestamps in like format , where any non-digit character can be used as a separator or separators can be omitted altogether. Additionally, only the date part is mandatory, all possibly missing time components are considered to be zeros.

Integers and floats are considered to represent seconds since the .

(Epoch time)
    Same string conversion as with , but all time components are expected to be omitted or to be zeros.
    , , Strings are expected to represent a time interval in one of the time formats Robot Framework supports: , or . Integers and floats are considered to be seconds. (42 seconds) (same as above)
 

Strings are converted to objects. On Windows is converted to \ automatically.

New in Robot Framework 6.0.

   

The specified type must be an enumeration (a subclass of or ) and given arguments must match its member names.

Matching member names is case, space, underscore and hyphen insensitive, but exact matches have precedence over normalized matches. Ignoring hyphens is new in Robot Framework 7.0.

Enumeration documentation and members are shown in documentation generated by automatically.

class Direction(Enum): """Move direction.""" NORTH = auto() NORTH_WEST = auto() def kw(arg: Direction): ... (Direction.NORTH) (Direction.NORTH_WEST)
    ,

The specified type must be an integer based enumeration (a subclass of or ) and given arguments must match its member names or values.

Matching member names works the same way as with . Values can be given as integers and as strings that can be converted to integers.

Enumeration documentation and members are shown in documentation generated by automatically.

New in Robot Framework 4.1.

class PowerState(IntEnum): """Turn system ON or OFF.""" OFF = 0 ON = 1 def kw(arg: PowerState): ... (PowerState.OFF) (PowerState.ON)
    Any

Only specified values are accepted. Values can be strings, integers, bytes, Booleans, enums and , and used arguments are converted using the value type specific conversion logic.

Strings are case, space, underscore and hyphen insensitive, but exact matches have precedence over normalized matches.

provides similar functionality as , but does not support custom documentation.

New in Robot Framework 7.0.

def kw(arg: Literal['ON', 'OFF']): ...
    String (case-insensitive) is converted to the Python object. Other values cause an error.
    Any

Any value is accepted. No conversion is done.

New in Robot Framework 6.1.

 
sequence ,

Strings must be Python list literals. They are converted to actual lists using the function. They can contain any values supports, including lists and other containers.

If the used type hint is (e.g. ), sequences that are not lists are converted to lists. If the type hint is generic , sequences are used without conversion.

Alias is new in Robot Framework 7.0.

    , Same as , but string arguments must tuple literals.
  , Same as , but string arguments must be set literals or to create an empty set.
    , Same as , but the result is a .
dictionary, mapping, map ,

Same as , but string arguments must be dictionary literals.

Alias is new in Robot Framework 7.0.

    ,

Same as , but dictionary items are also converted to the specified types and items not included in the type spec are not allowed.

New in Robot Framework 6.0. Normal conversion was used earlier.

class Config(TypedDict): width: int enabled: bool

Starting from Robot Framework 5.0, types that have a converted are automatically shown in Libdoc outputs.

Prior to Robot Framework 4.0, most types supported converting string NONE (case-insensitively) to Python None . That support has been removed and None conversion is only done if an argument has None as an explicit type or as a default value.

Specifying multiple possible types

Starting from Robot Framework 4.0, it is possible to specify that an argument has multiple possible types. In this situation argument conversion is attempted based on each type and the whole conversion fails if none of these conversions succeed.

When using function annotations, the natural syntax to specify that an argument has multiple possible types is using Union :

When using Python 3.10 or newer, it is possible to use the native type1 | type2 syntax instead:

Robot Framework 7.0 enhanced the support for the union syntax so that also "stringly typed" unions like 'type1 | type2' work. This syntax works also with older Python versions:

An alternative is specifying types as a tuple. It is not recommended with annotations, because that syntax is not supported by other tools, but it works well with the @keyword decorator:

With the above examples the length argument would first be converted to an integer and if that fails then to a float. The padding would be first converted to an integer, then to a string, and finally to None .

If the given argument has one of the accepted types, then no conversion is done and the argument is used as-is. For example, if the length argument gets value 1.5 as a float, it would not be converted to an integer. Notice that using non-string values like floats as an argument requires using variables as these examples giving different values to the length argument demonstrate:

If one of the accepted types is string, then no conversion is done if the given argument is a string. As the following examples giving different values to the padding argument demonstrate, also in these cases passing other types is possible using variables:

If the given argument does not have any of the accepted types, conversion is attempted in the order types are specified. If any conversion succeeds, the resulting value is used without attempting remaining conversions. If no individual conversion succeeds, the whole conversion fails.

If a specified type is not recognized by Robot Framework, then the original argument value is used as-is. For example, with this keyword conversion would first be attempted to an integer, but if that fails the keyword would get the original argument:

Starting from Robot Framework 6.1, the above logic works also if an unrecognized type is listed before a recognized type like Union[Unrecognized, int] . Also in this case int conversion is attempted, and the argument id passed as-is if it fails. With earlier Robot Framework versions, int conversion would not be attempted at all.

Type conversion with generics

With generics also the parameterized syntax like list[int] or dict[str, int] works. When this syntax is used, the given value is first converted to the base type and then individual items are converted to the nested types. Conversion with different generic types works according to these rules:

  • With lists there can be only one type like list[float] . All list items are converted to that type.
  • With tuples there can be any number of types like tuple[int, int] and tuple[str, int, bool] . Tuples used as arguments are expected to have exactly that amount of items and they are converted to matching types.
  • To create a homogeneous tuple, it is possible to use exactly one type and ellipsis like tuple[int, ...] . In this case tuple can have any number of items and they are all converted to the specified type.
  • With dictionaries there must be exactly two types like dict[str, int] . Dictionary keys are converted using the former type and values using the latter.
  • With sets there can be exactly one type like set[float] . Conversion logic is the same as with lists.

Using the native list[int] syntax requires Python 3.9 or newer. If there is a need to support also earlier Python versions, it is possible to either use matching types from the typing module like List[int] or use the "stringly typed" syntax like 'list[int]' .

Support for converting nested types with generics is new in Robot Framework 6.0. Same syntax works also with earlier versions, but arguments are only converted to the base type and nested types are not used for anything.

Support for "stringly typed" parameterized generics is new in Robot Framework 7.0.

Custom argument converters

In addition to doing argument conversion automatically as explained in the previous sections, Robot Framework supports custom argument conversion. This functionality has two main use cases:

  • Overriding the standard argument converters provided by the framework.
  • Adding argument conversion for custom types and for other types not supported out-of-the-box.

Argument converters are functions or other callables that get arguments used in data and convert them to desired format before arguments are passed to keywords. Converters are registered for libraries by setting ROBOT_LIBRARY_CONVERTERS attribute (case-sensitive) to a dictionary mapping desired types to converts. When implementing a library as a module, this attribute must be set on the module level, and with class based libraries it must be a class attribute. With libraries implemented as classes, it is also possible to use the converters argument with the @library decorator . Both of these approaches are illustrated by examples in the following sections.

Custom argument converters are new in Robot Framework 5.0.

Overriding default converters

Let's assume we wanted to create a keyword that accepts date objects for users in Finland where the commonly used date format is dd.mm.yyyy . The usage could look something like this:

Automatic argument conversion supports dates, but it expects them to be in yyyy-mm-dd format so it will not work. A solution is creating a custom converter and registering it to handle date conversion:

Conversion errors

If we try using the above keyword with invalid argument like invalid , it fails with this error:

This error is not too informative and does not tell anything about the expected format. Robot Framework cannot provide more information automatically, but the converter itself can be enhanced to validate the input. If the input is invalid, the converter should raise a ValueError with an appropriate message. In this particular case there would be several ways to validate the input, but using regular expressions makes it possible to validate both that the input has dots ( . ) in correct places and that date parts contain correct amount of digits:

With the above converter code, using the keyword with argument invalid fails with a lot more helpful error message:

Restricting value types

By default Robot Framework tries to use converters with all given arguments regardless their type. This means that if the earlier example keyword would be used with a variable containing something else than a string, conversion code would fail in the re.match call. For example, trying to use it with argument ${42} would fail like this:

This error situation could naturally handled in the converter code by checking the value type, but if the converter only accepts certain types, it is typically easier to just restrict the value to that type. Doing it requires only adding appropriate type hint to the converter:

Notice that this type hint is not used for converting the value before calling the converter, it is used for strictly restricting which types can be used. With the above addition calling the keyword with ${42} would fail like this:

If the converter can accept multiple types, it is possible to specify types as a Union . For example, if we wanted to enhance our keyword to accept also integers so that they would be considered seconds since the Unix epoch , we could change the converter like this:

Converting custom types

A problem with the earlier example is that date objects could only be given in dd.mm.yyyy format. It would not work if there was a need to support dates in different formats like in this example:

A solution to this problem is creating custom types instead of overriding the default date conversion:

Strict type validation

Converters are not used at all if the argument is of the specified type to begin with. It is thus easy to enable strict type validation with a custom converter that does not accept any value. For example, the Example keyword accepts only StrictType instances:

As a convenience, Robot Framework allows setting converter to None to get the same effect. For example, this code behaves exactly the same way as the code above:

Using None as a strict converter is new in Robot Framework 6.0. An explicit converter function needs to be used with earlier versions.

Accessing the test library from converter

Starting from Robot Framework 6.1, it is possible to access the library instance from a converter function. This allows defining dynamic type conversions that depend on the library state. For example, if the library can be configured to test particular locale, you might use the library state to determine how a date should be parsed like this:

The library argument to converter function is optional, i.e. if the converter function only accepts one argument, the library argument is omitted. Similar result can be achieved by making the converter function accept only variadic arguments, e.g. def parse_date(*varargs) .

Converter documentation

Information about converters is added to outputs produced by Libdoc automatically. This information includes the name of the type, accepted values (if specified using type hints) and documentation. Type information is automatically linked to all keywords using these types.

Used documentation is got from the converter function by default. If it does not have any documentation, documentation is got from the type. Both of these approaches to add documentation to converters in the previous example thus produce the same result:

Adding documentation is in general recommended to provide users more information about conversion. It is especially important to document converter functions registered for existing types, because their own documentation is likely not very useful in this context.

Although Robot Framework gets lot of information about keywords automatically, such as their names and arguments, there are sometimes needs to configure this information further. This is typically easiest done by using the robot.api.deco.keyword decorator. It has several useful usages that are explained thoroughly elsewhere and only listened here as a reference:

  • Exposing methods and functions as keywords when the automatic keyword discovery has been disabled by using the @library decorator or otherwise.
  • Setting a custom name to a keyword. This is especially useful when using the embedded argument syntax .
  • Setting keyword tags .
  • Setting type information to enable automatic argument type conversion. Supports also disabling the argument conversion altogether.
  • Marking methods to expose as keywords when using the dynamic library API or the hybrid library API .

The robot.api.deco.not_keyword decorator can be used for disabling functions or methods becoming keywords .

When implementing keywords, it is sometimes useful to modify them with Python decorators . However, decorators often modify function signatures and can thus confuse Robot Framework's introspection when determining which arguments keywords accept. This is especially problematic when creating library documentation with Libdoc and when using external tools like RIDE . The easiest way to avoid this problem is decorating the decorator itself using functools.wraps . Other solutions include using external modules like decorator and wrapt that allow creating fully signature-preserving decorators.

Support for "unwrapping" decorators decorated with functools.wraps is a new feature in Robot Framework 3.2.

Library keywords can also accept embedded arguments the same way as user keywords . This section mainly covers the Python syntax to use to create such keywords, the embedded arguments syntax itself is covered in detail as part of user keyword documentation .

Library keywords with embedded arguments need to have a custom name that is typically set using the @keyword decorator . Values matching embedded arguments are passed to the function or method implementing the keyword as positional arguments. If the function or method accepts more arguments, they can be passed to the keyword as normal positional or named arguments. Argument names do not need to match the embedded argument names, but that is generally a good convention.

Keywords accepting embedded arguments:

Tests using the above keywords:

If type information is specified, automatic argument conversion works also with embedded arguments:

Support for mixing embedded arguments and normal arguments is new in Robot Framework 7.0.

Starting from Robot Framework 6.1, it is possible to run native asynchronous functions (created by async def ) just like normal functions:

You can get the reference of the loop using asyncio.get_running_loop() or asyncio.get_event_loop() . Be careful when modifying how the loop runs, it is a global resource. For example, never call loop.close() because it will make it impossible to run any further coroutines. If you have any function or resource that requires the event loop, even though await is not used explicitly, you have to define your function as async to have the event loop available.

More examples of functionality:

Robot Framework waits for the function to complete. If you want to have a task that runs for a long time, use, for example, asyncio.create_task() . It is your responsibility to manage the task and save a reference to avoid it being garbage collected. If the event loop closes and a task is still pending, a message will be printed to the console.

If execution of keyword cannot continue for some reason, for example a signal stop, Robot Framework will cancel the async task and any of its children. Other async tasks will continue running normally.

After a method implementing a keyword is called, it can use any mechanism to communicate with the system under test. It can then also send messages to Robot Framework's log file, return information that can be saved to variables and, most importantly, report if the keyword passed or not.

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 .

Normal execution failures and errors can be reported using the standard exceptions such as AssertionError , ValueError and RuntimeError . There are, however, some special cases explained in the subsequent sections where special exceptions are needed.

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.

In all cases, it is important for the users that the exception message is as informative as possible.

HTML in error messages

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 .

Cutting long messages automatically

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.

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 .

Robot Framework provides some exceptions that libraries can use for reporting failures and other events. These exceptions are exposed via the robot.api package and contain the following:

All these exceptions are new in Robot Framework 4.0. Other features than skipping tests, which is also new in Robot Framework 4.0, are available by other means in earlier versions.

It is possible to continue test execution even when there are failures . The easiest way to do that is using the provided robot.api.ContinuableFailure exception:

An alternative is creating a custom exception that has a special ROBOT_CONTINUE_ON_FAILURE attribute set to a True value. This is demonstrated by the example below.

It is possible to skip tests with a library keyword. The easiest way to do that is using the provided robot.api.SkipExecution exception:

An alternative is creating a custom exception that has a special ROBOT_SKIP_EXECUTION attribute set to a True value. This is demonstrated by the example below.

It is possible to fail a test case so that the whole test execution is stopped . The easiest way to accomplish this is using the provided robot.api.FatalError exception:

In addition to using the robot.api.FatalError exception, it is possible create a custom exception that has a special ROBOT_EXIT_ON_FAILURE attribute set to a True value. This is illustrated by the example below.

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.

Using log levels

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 . In this formant *LEVEL* must be in the beginning of a line and LEVEL must be one of the available concrete log levels TRACE , DEBUG , INFO , WARN or ERROR , or a pseudo log level HTML or CONSOLE . The pseudo levels can be used for logging HTML and logging to console , respectively.

Errors and warnings

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.

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.

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:

As illustrated by the examples below, adding the timestamp is easy. It is, however, even easier to get accurate timestamps using the programmatic logging APIs . A big benefit of adding timestamps explicitly is that this approach works also with the remote library interface .

Logging to console

Libraries have several options for writing messages to the console. 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.

Starting from Robot Framework 6.1, libraries can use a pseudo log level CONSOLE for logging messages both to the log file and to the console:

These messages will be logged to the log file using the INFO level similarly as with the HTML pseudo log level. When using this approach, messages are logged to the console only after the keyword execution ends.

Another option 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:

The final option is using the public logging API . Also in with this approach messages are written to the console immediately:

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. Level CONSOLE can be used when the message needs to shown both in console and in the log file.

The following examples clarify how logging with different levels works.

16:18:42.123 INFO Hello from a library.
16:18:42.123 WARN Warning from a library.
16:18:42.123 ERROR Something unexpected happen that may indicate a problem in the test.
16:18:42.123 INFO Hello again!
This will be part of the previous message.
16:18:42.123 INFO This is a new message.
16:18:42.123 INFO This is <b>normal text</b>.
16:18:42.123 INFO This logs into console and log file.
16:18:42.123 INFO This is .
16:18:42.123 INFO

Programmatic APIs provide somewhat cleaner way to log information than using the standard output and error streams.

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:

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.

Using 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.

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.

Library logging using the logging API during import:

If you log something during initialization, i.e. in Python __init__ , the messages may be logged multiple times depending on the library scope .

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 in 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.

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 lists or list-like objects.

Starting from Robot Framework 6.1, it is easy to detect is Robot Framework running at all and is the dry-run mode active by using the robot_running and dry_run_active properties of the BuiltIn library. A relatively common use case is that library initializers may want to avoid doing some work if the library is not used during execution but is initialized, for example, by Libdoc :

For more information about using the BuiltIn library as a programmatic API, including another example using robot_running , see the Using BuiltIn library section.

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.

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 as in the example below.

Python has 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 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 .

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 the 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.

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.

Python has 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.

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 .

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.

20080911 16:00:22.650 WARN Keyword 'SomeLibrary.Example Keyword' is deprecated. Use keyword `Other Keyword` instead.

This deprecation system works with most test libraries and also with user keywords .

The dynamic API is in most ways similar to the static API. For example, reporting the keyword status, logging, and returning values works exactly the same way. Most importantly, there are no differences in importing dynamic libraries and using their keywords compared to other libraries. In other words, users do not need to know what APIs their libraries use.

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, 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. Python users may also find the PythonLibCore project useful.

Dynamic libraries tell what keywords they implement with the get_keyword_names method. 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.

Marking methods to expose as keywords

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 .

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 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.

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.

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.

Representing different arguments with
Argument type How to represent Examples
No arguments Empty list.
One or more List of strings containing argument names.

Two ways how to represent the argument name and the default value:

.

String with separator:

Tuple:

Arguments before the marker. New in Robot Framework 6.1.
(varargs) Argument after possible positional arguments has a prefix
Arguments after varargs or a lone if there are no varargs. With or without defaults. Requires to . New in Robot Framework 3.1.
(kwargs) Last arguments has prefix. Requires to .

When the get_keyword_arguments is used, Robot Framework automatically calculates how many positional arguments the keyword requires and does it support free named arguments or not. If a keyword is used with invalid arguments, an error occurs and run_keyword is not even called.

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.

As explained in the above table, default values can be specified with argument names either as a string like 'name=default' or as a tuple like ('name', 'default') . The main problem with the former syntax is that all default values are considered strings whereas the latter syntax allows using all objects like ('inteter', 1) or ('boolean', True) . When using other objects than strings, Robot Framework can do automatic argument conversion based on them.

For consistency reasons, also arguments that do not accept default values can be specified as one item tuples. For example, ['a', 'b=c', '*d'] and [('a',), ('b', 'c'), ('*d',)] are equivalent.

If get_keyword_arguments is missing or returns Python None 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.

Support to specify arguments as tuples like ('name', 'default') is new in Robot Framework 3.2. Support for positional-only arguments in dynamic library API is new in Robot Framework 6.1.

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.

Robot Framework does automatic argument conversion also based on the argument default values . Earlier this did not work with the dynamic API because it was possible to specify arguments only as strings. As discussed in the previous section , this was changed in Robot Framework 3.2 and nowadays default values returned like ('example', True) are automatically used for this purpose.

Starting from Robot Framework 7.0, dynamic libraries can also specify the keyword return type by using key 'return' with an appropriate type in the returned type dictionary. This information is not used for anything during execution, but it is shown by Libdoc for documentation purposes.

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 .

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. 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.

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.

The dynamic API masks the real implementation of keywords from Robot Framework and thus makes it impossible to see where keywords are implemented. This means that editors and other tools utilizing Robot Framework APIs cannot implement features such as go-to-definition. This problem can be solved by implementing yet another optional dynamic method named get_keyword_source (alias getKeywordSource ) that returns the source information.

The return value from the get_keyword_source method must be a string or None if no source information is available. In the simple case it is enough to simply return an absolute path to the file implementing the keyword. If the line number where the keyword implementation starts is known, it can be embedded to the return value like path:lineno . Returning only the line number is possible like :lineno .

The source information of the library itself is got automatically from the imported library class the same way as with other library APIs. The library source path is used with all keywords that do not have their own source path defined.

Returning source information for keywords is a new feature in Robot Framework 3.2.

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 ).

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.

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.

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.

All special methods in the dynamic API
Name Arguments Purpose
  of the implemented keywords.
with given arguments. is optional.
Return keywords' . Optional method.
Return keywords' . Optional method. New in RF 3.1.
Return keywords' . Optional method.
Return keywords' and library's . Optional method.
Return keywords' . Optional method. New in RF 3.2.

A good example of using the dynamic API is Robot Framework's own Remote library .

Starting from Robot Framework 7.0, dynamic libraries can have asynchronous implementations of their special methods.

The hybrid library API is, as its name implies, a hybrid between the static API and the dynamic API. Just as with the dynamic API, it is possible to implement a library using the hybrid API only as a class.

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.

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.

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 .

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.

When implementing a test library, 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. 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.

Test libraries can use Robot Framework's internal modules, for example, to get information about the executed tests and the settings that are used. This powerful mechanism to communicate with the framework should be used with care, though, because all Robot Framework's APIs are not meant to be used by externally and they might change radically between different framework versions.

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 .

As the above examples illustrates, BuiltIn also has a convenient robot_running property for detecting is Robot Framework running .

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.

This section explains different approaches how to add new functionality to existing test libraries and how to use them in your own libraries otherwise.

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.

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 .

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 .

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.

The remote library interface provides means for having test libraries on different machines than where Robot Framework itself is running, and also for implementing libraries using other languages than the natively supported Python. For a test library, user remote libraries look pretty much the same as any other test library, and developing test libraries using the remote library interface is also very close to creating normal test libraries .

Importing Remote library

Starting and stopping remote servers, required methods, using get_keyword_names and keyword specific getters, using get_library_information, executing remote keywords, different argument syntaxes.

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:

src/ExtendingRobotFramework/remote.png

Robot Framework architecture with Remote library

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 suite, or just want to give it a more descriptive name, you can give it an alias when importing it .

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.

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.

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 .

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 ).

Servers may be configured so that users cannot stop it with Stop Remote Server keyword or stop_remote_server method.

Because the XML-RPC protocol does not support all possible object types, the values transferred between the Remote library and remote servers must be converted to compatible types. This applies to the keyword arguments the Remote library passes to remote servers and to the return values servers give back to the Remote library.

Both the Remote library and the Python remote server handle Python values according to the following rules. Other remote servers should behave similarly.

  • Strings, numbers and Boolean values are passed without modifications.
  • Python None is converted to an empty string.
  • All lists, tuples, and other iterable objects (except strings and dictionaries) are passed as lists so that their contents are converted recursively.
  • Dictionaries and other mappings are passed as dicts so that their keys are converted to strings and values converted to supported types recursively.
  • Returned dictionaries are converted to so called dot-accessible dicts that allow accessing keys as attributes using the extended variable syntax like ${result.key} . This works also with nested dictionaries like ${root.child.leaf} .
  • Strings containing bytes in the ASCII range that cannot be represented in XML (e.g. the null byte) are sent as Binary objects that internally use XML-RPC base64 data type. Received Binary objects are automatically converted to byte strings.
  • Other types are converted to strings.

This section explains the protocol that is used between the Remote library and remote servers. This information is mainly targeted for people who want to create new remote servers.

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.

The Python remote server can be used as a reference implementation.

There are two possibilities how remote servers can provide information about the keywords they contain. They are briefly explained below and documented more thoroughly in the subsequent sections.

  • Remote servers can implement the same methods as the dynamic library API has. This means get_keyword_names method and optional get_keyword_arguments , get_keyword_types , get_keyword_tags and get_keyword_documentation methods. Notice that using "camel-case names" like getKeywordNames is not possible similarly as in the normal dynamic API.
  • Starting from Robot Framework 4.0, remote servers can have a single get_library_information method that returns all library and keyword information as a single dictionary. If a remote server has this method, the other getter methods like get_keyword_names are not used at all. This approach has the benefit that there is only one XML-RPC call to get information while the approach explained above requires several calls per keyword. With bigger libraries the difference can be significant.

Regardless how remote servers provide information about their keywords, they must have run_keyword method that is used when keywords are executed. 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.

This section explains how the Remote library gets keyword names and other information when the server implements get_keyword_names . The next sections covers using the newer get_library_info method.

The get_keyword_names method must return names of the keyword the server contains 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 and what they must return is explained in the table below.

Keyword specific getter methods
Method Return value
Arguments as a list of strings in the .
Type information as a list or dictionary of strings. See below for details.
Documentation as a string.
Tags as a list of strings.

Type information used for argument conversion 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 by the XML-RPC server, but an empty string can be used to indicate that certain argument does not have type information instead.

Argument conversion is supported also based on default values using the same logic as with normal libraries . For this to work, arguments with default values must be returned as tuples, not as strings, the same way as with dynamic libraries . For example, argument conversion works if argument information is returned like [('count', 1), ('caseless', True)] but not if it is ['count=1', 'caseless=True'] .

Remote servers can also provide general library documentation to be used when generating documentation with the Libdoc tool. This information is got by calling get_keyword_documentation with special values __intro__ and __init__ .

get_keyword_types is new in Robot Framework 3.1 and support for argument conversion based on defaults is new in Robot Framework 4.0.

The get_library_information method allows returning information about the whole library in one XML-RPC call. The information must be returned as a dictionary where keys are keyword names and values are nested dictionaries containing keyword information. The dictionary can also contain separate entries for generic library information.

The keyword information dictionary can contain keyword arguments, documentation, tags and types, and the respective keys are args , doc , tags and types . Information must be provided using same semantics as when get_keyword_arguments , get_keyword_documentation , get_keyword_tags and get_keyword_types discussed in the previous section. If some information is not available, it can be omitted from the info dictionary altogether.

get_library_information supports also returning general library documentation to be used with Libdoc . It is done by including special __intro__ and __init__ entries into the returned library information dictionary.

For example, a Python library like

could be mapped into this kind of library information dictionary:

get_library_information is new in Robot Framework 4.0.

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.

Entries in the remote result dictionary
Name Explanation
status Mandatory execution status. Either PASS or FAIL.
output Possible output to write into the log file. Must be given as a single string but can contain multiple messages and different in format . It is also possible to embed to the log messages like .
return Possible return value. Must be one of the .
error Possible error message. Used only when the execution fails.
traceback Possible stack trace to using DEBUG level when the execution fails.
continuable When set to , or any value considered in Python, the occurred failure is considered .
fatal Like , but denotes that the occurred failure is .

The Remote library is a dynamic library , and in general it handles different argument syntaxes according to the same rules as any other dynamic library. This includes mandatory arguments, default values, varargs, as well as named argument syntax .

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.

Robot Framework's listener interface provides a powerful mechanism for getting notifications and for inspecting and modifying data and results during execution. Listeners are called, for example, when suites, tests and keywords start and end, when output files are ready, and finally when the whole execution ends. Example usages include communicating with external test management systems, sending a message when a test fails, and modifying tests during execution.

Listeners are implemented as classes or modules with certain special methods. They can be taken into use from the command line and be registered by libraries . The former listeners are active during the whole execution while the latter are active only when executing suites where libraries registering them are imported.

There are two supported listener interface versions, listener version 2 and listener version 3 . They have mostly the same methods, but these methods are called with different arguments. The newer listener version 3 is more powerful and generally recommended.

Listener version 2

Listener version 3, registering listeners from command line, libraries as listeners, getting information, modifying data and results, more examples.

Listeners are implement as modules or classes similarly as libraries . They can implement certain named hook methods depending on what events they are interested in. For example, if a listener wants to get a notification when a test starts, it can implement the start_test method. As discussed in the subsequent sections, different listener versions have slightly different set of available methods and they also are called with different arguments.

Listeners do not need to implement any explicit interface, it is enough to simply implement needed methods and they will be recognized automatically. There are, however, base classes robot.api.interfaces.ListenerV2 and robot.api.interfaces.ListenerV3 that can be used to get method name completion in editors, type hints, and so on.

Optional listener base classes are new in Robot Framework 6.1.

In addition to using "snake case" like start_test with listener method names, it is possible to use "camel case" like startTest . This support was added when it was possible to run Robot Framework on Jython and implement listeners using Java. It is preserved for backwards compatibility reasons, but not recommended with new listeners.

There are two supported listener interface versions with version numbers 2 and 3. A listener can specify which version to use by having a ROBOT_LISTENER_API_VERSION attribute with value 2 or 3, respectively. Starting from Robot Framework 7.0, the listener version 3 is used by default if the version is not specified.

Listener version 2 and listener version 3 have mostly the same methods, but arguments passed to these methods are different. Arguments given to listener 2 methods are strings and dictionaries containing information about execution. This information can be inspected and sent further, but it is not possible to modify it directly. Listener 3 methods get the same model objects that Robot Framework itself uses, and these model objects can be both inspected and modified.

Listener version 3 is more powerful than the older listener version 2 and generally recommended.

Listeners using the listener API version 2 get notifications about various events during execution, but they do not have access to actually executed tests and thus cannot directly affect the execution or created results.

Listener methods in the API version 2 are listed in the following table and in the API docs of the optional ListenerV2 base class. 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.

Methods in the listener API 2
Method Arguments Documentation
start_suite name, attributes

Called when a test suite starts.

Contents of the attribute dictionary:

: Suite id. for the top level suite, for its first child suite, for the second child, and so on. : Suite name including parent suites. : Suite documentation. : as a dictionary. : An absolute path of the file/directory the suite was created from. : Names of the direct child suites this suite has as a list. : Names of the tests this suite has as a list. Does not include tests of the possible child suites. : The total number of tests in this suite. and all its sub-suites as an integer. : Suite execution start time.
end_suite name, attributes

Called when a test suite ends.

Contents of the attribute dictionary:

: Same as in . : Same as in . : Same as in . : Same as in . : Same as in . : Same as in . : Suite execution end time. : Total execution time in milliseconds as an integer : Suite status as string , or . : Suite statistics (number of passed and failed tests in the suite) as a string. : Error message if suite setup or teardown has failed, empty otherwise.
start_test name, attributes

Called when a test case starts.

Contents of the attribute dictionary:

: Test id in format like , where the beginning is the parent suite id and the last part shows test index in that suite. : Test name including parent suites. : Test name with possible variables unresolved. New in RF 3.2. : Test documentation. : Test tags as a list of strings. : The name of the template used for the test. An empty string if the test not templated. : An absolute path of the test case source file. New in RF 4.0. : Line number where the test starts in the source file. New in RF 3.2. : Test execution execution start time.
end_test name, attributes

Called when a test case ends.

Contents of the attribute dictionary:

: Same as in . : Same as in . : Same as in . : Same as in . : Same as in . : Same as in . : Same as in . : Same as in . : Same as in . : Test execution execution end time. : Total execution time in milliseconds as an integer : Test status as string , or . : Status message. Normally an error message or an empty string.
start_keyword name, attributes

Called when a keyword or a control structure such as or starts.

With keywords is the full keyword name containing possible library or resource name as a prefix like . With control structures contains string representation of parameters.

Keywords and control structures share most of attributes, but control structures can have additional attributes depending on their .

Shared attributes:

: String specifying type of the started item. Possible values are: , , , , , , , , , , , , , , , and . All type values were changed in RF 4.0 and in RF 5.0 was changed to . : Name of the keyword without library or resource prefix. String representation of parameters with control structures. : Name of the library or resource file the keyword belongs to. An empty string with user keywords in a test case file and with control structures. : Keyword documentation. : Keyword's arguments as a list of strings. : A list of variable names that keyword's return value is assigned to. : as a list of strings. : An absolute path of the file where the keyword was used. New in RF 4.0. : Line where the keyword was used. New in RF 4.0. : Initial keyword status. if keyword is not executed (e.g. due to an earlier failure), otherwise. New in RF 4.0. : Keyword execution start time.

Additional attributes for types:

: Assigned variables for each loop iteration as a list or strings. : Type of loop (e.g. ). : List of values being looped over as a list or strings. : Start configuration. Only used with loops. New in RF 6.1. : Mode configuration. Only used with loops. New in RF 6.1. : Fill value configuration. Only used with loops. New in RF 6.1.

Additional attributes for types with loops:

: Variables and string representations of their contents for one loop iteration as a dictionary.

Additional attributes for types:

: The looping condition. : The maximum iteration limit. : What to do if the limit is exceeded. Valid values are and . New in RF 7.0. : The custom error raised when the limit of the WHILE loop is reached. New in RF 6.1.

Additional attributes for and types:

: The conditional expression being evaluated. With new in RF 6.1.

Additional attributes for types:

: The exception patterns being matched as a list or strings. : The type of pattern match (e.g. ). : The variable containing the captured exception.

Additional attributes for types:

: Return values from a keyword as a list or strings.

Additional attributes for types:

: Variable name. : Variable value. A string with scalar variables and a list otherwise. : Variable scope (e.g. ) as a string.

Additional attributes for control structures are in general new in RF 6.0. is new in RF 7.0.

end_keyword name, attributes

Called when a keyword or a control structure ends.

is the full keyword name containing possible library or resource name as a prefix. For example, .

Control structures have additional attributes, which change based on the attribute. For descriptions of all possible attributes, see the section.

Contents of the attribute dictionary:

: Same as with . : Same as with . : Same as with . : Same as with . : Same as with . : Same as with . : Same as with . : Same as with . : Same as with . : Same as with . : Keyword execution end time. : Total execution time in milliseconds as an integer : Keyword status as string , , or . and are new in RF 4.0.
log_message message

Called when an executed keyword writes a log message.

is a dictionary with the following contents:

: The content of the message. : used in logging the message. : Message creation time in format . : String or denoting whether the message should be interpreted as HTML or not.

Not called if the message level is below the current .

message message

Called when the framework itself writes a message.

is a dictionary with the same contents as with method.

library_import name, attributes

Called when a library has been imported.

is the name of the imported library. If the library has been given a custom name when imported it using , is the specified alias.

Contents of the attribute dictionary:

: Arguments passed to the library as a list. : The original library name if the library has been given an alias using , otherwise same as . : An absolute path to the library source. An empty string if getting the source of the library failed for some reason. : An absolute path to the file importing the library. when is imported as well as when using the Import Library keyword.
resource_import name, attributes

Called when a resource file has been imported.

is the name of the imported resource file without the file extension.

Contents of the attribute dictionary:

: An absolute path to the imported resource file. : An absolute path to the file importing the resource file. when using the Import Resource keyword.
variables_import name, attributes

Called when a variable file has been imported.

is the name of the imported variable file with the file extension.

Contents of the attribute dictionary:

: Arguments passed to the variable file as a list. : An absolute path to the imported variable file. : An absolute path to the file importing the resource file. when using the Import Variables keyword.
output_file path

Called when writing to an is ready.

is an absolute path to the file as a string.

log_file path

Called when writing to a is ready.

is an absolute path to the file as a string.

report_file path

Called when writing to a is ready.

is an absolute path to the file as a string.

xunit_file path

Called when writing to an is ready.

is an absolute path to the file as a string.

debug_file path

Called when writing to a is ready.

is an absolute path to the file as a string.

close  

Called when the whole test execution ends.

With called when the library goes out of scope.

Listener version 3 has mostly the same methods as listener version 2 , but arguments of the methods related to test execution are different. These methods get actual running and result model objects that used by Robot Framework itself, and listeners can both query information they need and change the model objects on the fly.

Listener version 3 was enhanced heavily in Robot Framework 7.0 when it got methods related to keywords and control structures. It was enhanced further in Robot Framework 7.1 when it got methods related to library, resource file and variable file imports.

Listener version 3 has separate methods for library keywords, user keywords and all control structures. If there is a need to listen to all keyword related events, it is possible to implement start_keyword and end_keyword . In addition to that, start_body_item and end_body_item can be implemented to get notifications related to all keywords and control structures. These higher level listener methods are not called if more specific methods like start_library_keyword or end_if are implemented.

Listener methods in the API version 3 are listed in the following table and in the API docs of the optional ListenerV3 base class.

Methods in the listener API 3
Method Arguments Documentation
start_suite data, result

Called when a test suite starts.

and are model objects representing the and , respectively.

end_suite data, result

Called when a test suite ends.

Same arguments as with .

start_test data, result

Called when a test case starts.

and are model objects representing the and , respectively.

end_test data, result

Called when a test case ends.

Same arguments as with .

start_keyword data, result

Called when a keyword starts.

and are model objects representing the and , respectively.

This method is called, by default, with user keywords, library keywords and when a keyword call is invalid. It is not called if a more specific , or method is implemented.

end_keyword data, result

Called when a keyword ends.

Same arguments and other semantics as with .

start_user_keyword data, implementation, result

Called when a user keyword starts.

and are the same as with and is the actually executed .

If this method is implemented, is not called with user keywords.

end_user_keyword data, implementation, result

Called when a user keyword ends.

Same arguments and other semantics as with .

start_library_keyword data implementation, result

Called when a library keyword starts.

and are the same as with and represents the executed .

If this method is implemented, is not called with library keywords.

end_library_keyword data, implementation, result

Called when a library keyword ends.

Same arguments and other semantics as with .

start_invalid_keyword data implementation, result

Called when an invalid keyword call starts.

and are the same as with and represents the . Keyword may not have been found, there could have been multiple matches, or the keyword call itself could have been invalid.

If this method is implemented, is not called with invalid keyword calls.

end_invalid_keyword data, implementation, result

Called when an invalid keyword call ends.

Same arguments and other semantics as with .

start_for, start_for_iteration, start_while, start_while_iteration, start_if, start_if_branch, start_try, start_try_branch, start_var, start_continue, start_break, start_return data, result

Called when control structures start.

See the documentation and type hints of the optional base class for more information.

end_for, end_for_iteration, end_while, end_while_iteration, end_if, end_if_branch, end_try, end_try_branch, end_var, end_continue, end_break, end_return data, result

Called when control structures end.

See the documentation and type hints of the optional base class for more information.

start_error data, result Called when invalid syntax starts.
end_error data, result Called when invalid syntax ends.
start_body_item data, result Called when a keyword or a control structure starts, unless a more specific method such as or is implemented.
end_body_item data, result Called when a keyword or a control structure ends, unless a more specific method such as or is implemented.
log_message message

Called when an executed keyword writes a log message. is a model object representing the .

This method is not called if the message has level below the current .

message message

Called when the framework itself writes a message.

is same object as with .

library_import library, importer

Called after a library has been imported.

represents the imported library. It can be inspected and also modified. contains information about the location where the library was imported.

resource_import resource, importer

Called after a resource file has been imported.

represents the imported resource file. It can be inspected and also modified. contains information about the location where the resource was imported.

variables_import attrs, importer

Called after a variable file has been imported.

contains information about the imported variable file as a dictionary. It can be inspected, but modifications to it have no effect. contains information about the location where the variable file was imported.

This method will be changed in the future so that the dictionary is replaced with an object representing the imported variable file.

output_file path

Called when writing to an is ready.

is an absolute path to the file as a object.

log_file path

Called when writing to a is ready.

is an absolute path to the file as a object.

report_file path

Called when writing to a is ready.

is an absolute path to the file as a object.

xunit_file path

Called when writing to an is ready.

is an absolute path to the file as a object.

debug_file path

Called when writing to a is ready.

is an absolute path to the file as a object.

close  

Called when the whole test execution ends.

With called when the library goes out of scope.

Methods related to keywords and control structures are new in Robot Framework 7.0.

Methods related to library, resource file and variable file imports are new in Robot Framework 7.1.

Prior to Robot Framework 7.0, paths passed to result file related listener version 3 methods were strings.

Listeners that need to be active during the whole execution must be taken into use from the command line. That is done using the --listener option so that the name of the listener is given to it as an argument. The listener name is got from the name of the class or module implementing the listener, similarly as library name is got from the class or module implementing the library. The specified listeners must be in the same module search path where test libraries are searched from when they are imported. In addition to registering a listener by using a name, it is possible to give an absolute or a relative path to the listener file similarly as with test libraries . It is possible to take multiple listeners into use by using this option several times:

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:

In addition to passing arguments one-by-one as positional arguments, it is possible to pass them using the named argument syntax similarly as when using keywords:

Listener arguments are automatically converted using same rules as with keywords based on type hints and default values . For example, this listener

could be used like

and the first argument would be converted to an integer based on the type hint and the second to a Boolean based on the default value.

Both the named argument syntax and argument conversion are new in Robot Framework 4.0.

Sometimes it is useful also for test libraries to get notifications about test execution. This allows them, for example, to perform certain clean-up activities automatically when a test suite or the whole test execution ends.

Registering listener

A test library can register a listener by using the 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 it is possible to use _end_suite .

Following examples illustrates using an external listener as well as a library acting as a listener itself:

As the second example above already demonstrated, library listeners can specify listener interface versions using the ROBOT_LISTENER_API_VERSION attribute exactly like any other listener.

Starting from Robot Framework 7.0, a listener can register itself to be a listener also by using a string SELF (case-insensitive) as a listener. This is especially convenient when using the @library decorator :

It is also possible to specify multiple listeners for a single library by giving ROBOT_LIBRARY_LISTENER a value as a list:

Called listener methods

Library listeners get notifications about all events in suites where libraries using them are imported. In practice this means that suite, test, keyword, control structure and log message related methods are called. In addition to them, the close method is called when the library goes out of the scope.

If 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 library scope .

By default, listeners are called in the order they are taken into use so that listeners registered from the command line are called before library listeners. It is, however, possible to control the calling order by setting the special ROBOT_LISTENER_PRIORITY attribute to an integer or a floating point value. The bigger the number, the higher precedence the listener has and the earlier it is called. The number can be positive or negative and it is zero by default.

The custom order does not affect the close method of library listeners, though. That method is always called when the library goes out of its scope.

Controlling listener calling order is new in Robot Framework 7.1.

This section contains examples using the listener interface. First examples illustrate getting notifications durin execution and latter examples modify executed tests and created results.

The first example is implemented as a Python module. It uses the listener version 2 , but could equally well be implemented by using the listener version 3 .

If the above example would be saved to, for example, PauseExecution.py file, it could be used from the command line like this:

The next example, which still uses the listener version 2, 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 it 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.

The following 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 .

Modifying executed suites and tests

Changing what is executed is as easy as modifying the model objects representing executed data passed to listener methods. This is illustrated by the example below that adds a new test to each executed suite and a new keyword call to each test.

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:

Accessing library or resource file

It is possible to get more information about the actually executed keyword and the library or resource file it belongs to:

As the above example illustrates, it is possible to get an access to the actual library instance. This means that listeners can inspect the library state and also modify it. With user keywords it is even possible to modify the keyword itself or, via the owner resource file, any other keyword in the resource file.

Modifying results

Test execution results can be altered by modifying the result objects passed to listener methods. This is demonstrated by the following listener that is implemented as a class and also uses type hints:

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.

Changing keyword and control structure status

Listeners can also affect the execution flow by changing statuses of the executed keywords and control structures. For example, if a listener changes the status of a passed keyword to FAIL, the keyword is considered failed exactly as if it had failed normally. Similarly, it is possible to change the status of a passed or failed keyword to SKIP to get the keyword and the whole test skipped. It is also possible to silence failures by changing the status to PASS, but this should be done only in special cases and with great care to avoid hiding real failures.

The following example demonstrates changing the status by failing keywords that take too long time to execute. The previous example had similar logic with tests, but this listener also stops the execution immediately if there is a keyword that is too slow. As the example shows, listeners can also change the error message, not only the status.

Changes to status only affect the execution flow starting from Robot Framework 7.1.

Keyword and control structure related listener version 3 methods are so versatile that covering them fully here in the User Guide is not possible. For more examples, you can see the acceptance tests using theses methods in various ways.

Robot Framework supports external parsers that can handle custom data formats or even override Robot Framework's own parser.

Custom parsers are new in Robot Framework 6.1.

EXTENSION or extension attribute

Parse method, parse_init method, optional base class, parser implemented as module, parser implemented as class, parser extending optional base class, parser as preprocessor.

Parsers are taken into use from the command line with the --parser option using exactly the same semantics as with listeners . This includes specifying parsers as names or paths, giving arguments to parser classes, and so on:

Parsers can be implemented both as modules and classes. This section explains what attributes and methods they must contain.

This attribute specifies what file extension or extensions the parser supports. Both EXTENSION and extension names are accepted, and the former has precedence if both exist. The attribute can be either a string or a sequence of strings. Extensions are case-insensitive and can be specified with or without the leading dot. If a parser is implemented as a class, it is possible to set this attribute either as a class attribute or as an instance attribute.

Also extensions containing multiple parts like .example.ext or .robot.zip are supported.

If a parser supports the .robot extension, it will be used for parsing these files instead of the standard parser.

The mandatory parse method is responsible for parsing suite files . It is called with each parsed file that has an extension that the parser supports. The method must return a TestSuite object.

In simple cases parse can be implemented so that it accepts just a single argument that is a pathlib.Path object pointing to the file to parse. If the parser is interested in defaults for Test Setup , Test Teardown , Test Tags and Test Timeout set in higher level suite initialization files , the parse method must accept two arguments. In that case the second argument is a TestDefaults object.

The optional parse_init method is responsible for parsing suite initialization files i.e. files in format __init__.ext where .ext is an extension supported by the parser. The method must return a TestSuite object representing the whole directory. Suites created from child suite files and directories will be added to its child suites.

Also parse_init can be implemented so that it accepts one or two arguments, depending on is it interested in test related default values or not. If it accepts defaults, it can manipulate the passed TestDefaults object and changes are seen when parsing child suite files.

This method is only needed if a parser needs to support suite initialization files.

Parsers do not need to implement any explicit interface, but it may be helpful to extend the optional Parser base class. The main benefit is that the base class has documentation and type hints. It also works as a bit more formal API specification.

The first example demonstrates a simple parser implemented as a module and supporting one hard-coded extension. It just creates a dummy suite and does not actually parse anything.

The second parser is implemented as a class that accepts the extension to use as an argument. The parser reads the given source file and creates dummy tests from each line it contains.

This parser extends the optional Parser base class. It supports parsing suite initialization files, uses TestDefaults and registers multiple extensions.

The final example parser acts as a preprocessor for Robot Framework data files that supports headers in format === Test Cases === in addition to *** Test Cases *** . In this kind of usage it is convenient to use TestSuite.from_string , TestSuite.from_model and TestSuite.from_file_system factory methods for constructing the returned suite.

5   Supporting Tools

5.1   library documentation tool (libdoc), 5.2   test data documentation tool (testdoc).

Libdoc is Robot Framework's built-in tool that can generate documentation for Robot Framework libraries and resource files. It can generate HTML documentation for humans as well as machine readable spec files in XML and JSON formats. Libdoc also has few special commands to show library or resource information on the console.

Documentation can be created for:

  • libraries implemented using the normal static library API ,
  • libraries using the dynamic API , including remote libraries,
  • resource files ,
  • suite files , and
  • suite initialization files .

Additionally it is possible to use XML and JSON spec files created by Libdoc earlier as an input.

Support for generating documentation for suite files and suite initialization files is new in Robot Framework 6.0.

The support for the JSON spec files is new in Robot Framework 4.0.

-f , --format <html|xml|json|libspec>   Specifies whether to generate an HTML output for humans or a machine readable spec file in XML or JSON format. The libspec format means XML spec with documentations converted to HTML. The default format is got from the output file extension. -s , --specdocformat <raw|html>   Specifies the documentation format used with XML and JSON spec files. raw means preserving the original documentation format and html means converting documentation to HTML. The default is raw with XML spec files and html with JSON specs and when using the special libspec format. New in Robot Framework 4.0. -F , --docformat <robot|html|text|rest>   Specifies the source documentation format. Possible values are Robot Framework's documentation format, HTML, plain text, and reStructuredText. Default value can be specified in test library source code and the initial default value is robot . --theme <dark|light|none>   Use dark or light HTML theme. If this option is not used, or the value is none , the theme is selected based on the browser color scheme. Only applicable with HTML outputs. New in Robot Framework 6.0. -N , --name <newname>   Sets the name of the documented library or resource. -V , --version <newversion>   Sets the version of the documented library or resource. The default value for test libraries is defined in the source code . -P , --pythonpath <path>   Additional locations where to search for libraries and resources similarly as when running tests . --quiet Do not print the path of the generated output file to the console. New in Robot Framework 4.0. -h , --help Prints this help.

Executing Libdoc

The easiest way to run Libdoc is using the libdoc command created as part of the normal installation:

Alternatively it is possible to execute the robot.libdoc module directly. This approach is especially useful if you have installed Robot Framework using multiple Python versions and want to use a specific version with Libdoc:

Yet another alternative is running the robot.libdoc module as a script:

The separate libdoc command is new in Robot Framework 4.0.

Specifying library or resource file

Python libraries and dynamic libraries with name or path.

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 when importing libraries 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.

Resource files with path

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.

Libdoc spec files

Earlier generated Libdoc XML or JSON spec files can also be used as inputs. This works if spec files use either *.xml , *.libspec or *.json extension:

Support for the *.libspec extension is new in Robot Framework 3.2.

Support for the *.json extension is new in Robot Framework 4.0.

Generating documentation

Libdoc can generate documentation in HTML (for humans) and XML or JSON (for tools) formats. The file where to write the documentation is specified as the second argument after the library/resource name or path, and the output format is got from the output file extension by default.

Libdoc HTML documentation

Most Robot Framework libraries use Libdoc to generate library documentation in HTML format. This format is thus familiar for most people who have used Robot Framework. A simple example can be seen below, and it has been generated based on the example found a bit later in this section .

src/SupportingTools/ExampleLibrary.png

The HTML documentation starts with general library introduction, continues with a section about configuring the library when it is imported (when applicable), and finally has shortcuts to all keywords and the keywords themselves. The magnifying glass icon on the lower right corner opens the keyword search dialog that can also be opened by simply pressing the s key.

Libdoc automatically creates HTML documentation if the output file extension is *.html . If there is a need to use some other extension, the format can be specified explicitly with the --format option.

Libdoc XML spec files

Libdoc can also generate documentation in XML format that is suitable for external tools such as editors. It contains all the same information as the HTML format but in a machine readable format.

XML spec files also contain library and keyword source information so that the library and each keyword can have source path ( source attribute) and line number ( lineno attribute). The source path is relative to the directory where the spec file is generated thus does not refer to a correct file if the spec is moved. The source path is omitted with keywords if it is the same as with the library, and both the source path and the line number are omitted if getting them from the library fails for whatever reason.

Libdoc automatically uses the XML format if the output file extension is *.xml or *.libspec . When using the special *.libspec extension, Libdoc automatically enables the options -f XML -s HTML which means creating an XML output file where keyword documentation is converted to HTML. If needed, the format can be explicitly set with the --format option.

The exact Libdoc spec file format is documented with an XML schema (XSD) at https://github.com/robotframework/robotframework/tree/master/doc/schema . The spec file format may change between Robot Framework major releases.

To make it easier for external tools to know how to parse a certain spec file, the spec file root element has a dedicated specversion attribute. It was added in Robot Framework 3.2 with value 2 and earlier spec files can be considered to have version 1 . The spec version will be incremented in the future if and when changes are made. Robot Framework 4.0 introduced new spec version 3 which is incompatible with earlier versions.

The XML:HTML format introduced in Robot Framework 3.2. has been replaced by the format LIBSPEC ot the option combination --format XML --specdocformat HTML .

Including source information and spec version are new in Robot Framework 3.2.

Libdoc JSON spec files

Since Robot Framework 4.0 Libdoc can also generate documentation in JSON format that is suitable for external tools such as editors or web pages. It contains all the same information as the HTML format but in a machine readable format.

Similar to XML spec files the JSON spec files contain all information and can also be used as input to Libdoc. From that format any other output format can be created. By default the library documentation strings are converted to HTML format within the JSON output file.

The exact JSON spec file format is documented with an JSON schema at https://github.com/robotframework/robotframework/tree/master/doc/schema . The spec file format may change between Robot Framework major releases.

Viewing information on console

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.

Optional patterns given to list and show are case and space insensitive. Both also accept * and ? as wildcards.

This section discusses writing documentation for Python based test libraries that use the static library API as well as for dynamic libraries and resource files . Creating test libraries and resource files is described in more details elsewhere in the User Guide.

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. How the HTML documentation generated based on this example looks like can be seen above , and there is also a bit longer example at the end of this chapter.

If you library does some initialization work that should not be done when using Libdoc, you can easily detect is Robot Framework running

For more information on Python documentation strings, see PEP-257 .

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. If the library has an __init__ method that takes arguments in addition to self , its documentation and arguments are shown.

Resource file documentation

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 section for documenting the whole resource file.

Possible variables in resource files can not be documented.

Libdoc supports documentation in Robot Framework's own documentation syntax , HTML, plain text, and reStructuredText . The format to use can be specified in library source code using ROBOT_LIBRARY_DOC_FORMAT attribute or given from the command line using --docformat (-F) option. In both cases the possible case-insensitive values are ROBOT (default), HTML , TEXT and reST .

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.

Robot Framework documentation syntax

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.

Creating table of contents automatically

With bigger libraries it is often useful to add a table of contents to the library introduction. When using the Robot Framework documentation format, this can be done automatically by adding a special %TOC% marker into a line on its own. The table of contents is created based on the top-level section titles (e.g. = Section = ) used in the introduction. In addition to them, the TOC also gets links to the automatically created sections for shortcuts and keywords as well as for importing and tags sections when applicable.

Automatic TOC generation is a new feature in Robot Framework 3.2.

HTML documentation syntax

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 .

Plain text documentation syntax

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` .

reStructuredText documentation syntax

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. Syntax highlight requires additional Pygments module and supports all the languages that Pygments supports.

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.

When using reStructuredText documentation syntax , backticks must be escaped like \`Keyword Name\` .

Linking to automatic sections

The documentation generated by Libdoc always contains sections for overall library introduction and for keywords. If a library itself takes arguments, there is also separate importing section . If any of the keywords has tags , a separate selector for them is also shown in the overview.

All the 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.

Automatic section link targets
Section Target
Introduction `introduction` and `library introduction`
Importing `importing` and `library importing`
Keywords `keywords`

Before Robot Framework 4.0 there were also sections for tags and shortcuts. In Robot Framework 4.0 these have been removed in favor of the overview menu. This means that prior linking to shortcuts or tags sections does not work.

Linking to custom sections

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:

Linking to custom sections works only when using Robot Framework documentation syntax .

Libdoc shows information about keywords' arguments automatically.

Included information

The following information is shown for all keywords regardless are they implemented in libraries or in resource files:

  • Argument name. User keyword arguments are shown without the ${} decoration to make arguments look the same regardless where keywords originate from.
  • Marker telling is the argument positional-only , named-only , free positional , free named , or normal argument that can be given either by position or by name.
  • Possible default value. Shown like = 42 .
  • Possible type. Shown like <int> . Can be a link to type documentation as explained in the next section.

When referring to arguments in keyword documentation, it is recommended to use inline code style like ``argument`` .

Automatically listing type documentation

As mentioned above, Libdoc automatically shows possible type information when listing arguments. If the type is a custom type based on Enum or TypedDict , the type is automatically converted , or the type has custom converter , also the type itself is listed separately to show more information about it. When these types are used in arguments, the type name also becomes a link to the type information.

All listed data types show possible type documentation as well as what argument types are supported. In addition to that, types based on Enum list available members and types based on TypedDict show the dictionary structure.

Automatically listing types based on Enum and TypedDict is new in Robot Framework 4.0. Listing other types is new in Robot Framework 5.0.

The following example illustrates how to use the most important documentation formatting possibilities, internal linking , and so on. Click here to see how the generated documentation looks like.

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.

-T , --title <title>   Set the title of the generated documentation. Underscores in the title are converted to spaces. The default title is the name of the top level suite. -N , --name <name>   Override the name of the top level test suite. -D , --doc <doc>   Override the documentation of the top level test suite. -M , --metadata <name:value>   Set/override free metadata of the top level test suite. -G , --settag <tag>   Set given tag(s) to all test cases. -t , --test <name>   Include tests by name. -s , --suite <name>   Include suites by name. -i , --include <tag>   Include tests by tags. -e , --exclude <tag>   Exclude tests by tags. -A , --argumentfile <path>   Text file to read more arguments from. Works exactly like argument files when running tests. -h , --help Print this help in the console.

All options except --title have exactly the same semantics as same options have when executing test cases .

Data can be given as a single file, directory, or as multiple files and directories. In all these cases, the last argument must be the file where to write the output.

Testdoc can be executed as an installed module like python -m robot.testdoc or as a script like python path/robot/testdoc.py .

The built-in Tidy tool was deprecated in Robot Framework 4.1 in favor of the new and enhanced external Robotidy tool. It was removed altogether in Robot Framework 5.0.

There are plenty of external tools that can be used with Robot Framework. These tools include plugins for various IDEs and text editors, tools for parallel execution and data-driven testing, plugins for continuous integration systems, and so on.

These tools are developed as separate projects independently from Robot Framework itself. For a list of the available tools see http://robotframework.org/ .

6   Appendices

6.1   available settings, 6.2   command line options, 6.3   translations, 6.4   documentation formatting, 6.5   time format, 6.7   evaluating expressions, 6.8   registrations.

This appendix lists all settings that can be used in different sections.

Settings can be localized . See the Translations appendix for supported translations.

The Setting section is used to import libraries, resource files and variable files and to define metadata for test suites and test cases. It can be included in test case files and resource files. Note that in a resource file, a Setting section can only include settings for importing libraries, resources, and variables.

Settings available in the Setting section
Name Description
Library Used for .
Resource Used for .
Variables Used for .
Name Used for setting a custom .
Documentation Used for specifying a or documentation.
Metadata Used for setting .
Suite Setup Used for specifying the .
Suite Teardown Used for specifying the .
Test Tags Used for specifying for all tests in a suite.
Force Tags, Default Tags for specifying test case tags.
Keyword Tags Used for specifying for all keywords in a certain file.
Test Setup Used for specifying a default .
Test Teardown Used for specifying a default .
Test Template Used for specifying a default for test cases.
Test Timeout Used for specifying a default .
Task Setup, Task Teardown, Task Template, Task Timeout Aliases for Test Setup, Test Teardown, Test Template and Test Timeout, respectively, that can be used when .

The settings in the Test Case section are always specific to the test case for which they are defined. Some of these settings override the default values defined in the Settings section.

Exactly same settings are available when creating tasks in the Task section.

Settings available in the Test Case section
Name Description
[Documentation] Used for specifying a .
[Tags] Used for .
[Setup] Used for specifying a .
[Teardown] Used for specifying a .
[Template] Used for specifying a .
[Timeout] Used for specifying a .

Settings in the Keyword section are specific to the user keyword for which they are defined.

Settings available in the Keyword section
Name Description
[Documentation] Used for specifying a .
[Tags] Used for specifying .
[Arguments] Used for specifying .
[Setup] Used for specifying a . New in Robot Framework 7.0.
[Teardown] Used for specifying .
[Timeout] Used for specifying a .
[Return] Used for specifying . Deprecated in Robot Framework 7.0. Use the statement instead.

This appendix lists all the command line options that are available when executing test cases and when post-processing outputs . Also environment variables affecting execution and post-processing are listed.

--rpa Turn on generic automation mode. --language <lang>   Activate localization . lang can be a name or a code of a built-in language , or a path or a module name of a custom language file. -F , --extension <value>   Parse only these files when executing a directory. -I , --parseinclude <pattern>   Parse only matching files when executing a directory. -N , --name <name>   Sets the name of the top-level test suite. -D , --doc <document>   Sets the documentation of the top-level test suite. -M , --metadata <name:value>   Sets free metadata for the top level test suite. -G , --settag <tag>   Sets the tag(s) to all executed test cases. -t , --test <name>   Selects the test cases by name . --task <name> Alias for --test that can be used when executing tasks . -s , --suite <name>   Selects the test suites by name. -R , --rerunfailed <file>   Selects failed tests from an earlier output file to be re-executed. -S , --rerunfailedsuites <file>   Selects failed test suites from an earlier output file to be re-executed. -i , --include <tag>   Selects the test cases by tag. -e , --exclude <tag>   Selects the test cases by tag. --skip <tag> Tests having given tag will be skipped . Tag can be a pattern. --skiponfailure <tag>   Tests having given tag will be skipped if they fail. -v , --variable <name:value>   Sets individual variables . -V , --variablefile <path:args>   Sets variables using variable files . -d , --outputdir <dir>   Defines where to create output files . -o , --output <file>   Sets the path to the generated output file . --legacyoutput Creates output file in Robot Framework 6.x compatible format . -l , --log <file>   Sets the path to the generated log file . -r , --report <file>   Sets the path to the generated report file . -x , --xunit <file>   Sets the path to the generated xUnit compatible result file . -b , --debugfile <file>   A debug file that is written during execution. -T , --timestampoutputs   Adds a timestamp to output files listed above. --splitlog Split log file into smaller pieces that open in browser transparently. --logtitle <title>   Sets a title for the generated test log. --reporttitle <title>   Sets a title for the generated test report. --reportbackground <colors>   Sets background colors of the generated report. --maxerrorlines <lines>   Sets the number of error lines shown in report when tests fail. --maxassignlength <characters>   Sets the number of characters shown in log when variables are assigned . -L , --loglevel <level>   Sets the threshold level for logging. Optionally the default visible log level can be given separated with a colon (:). --suitestatlevel <level>   Defines how many levels to show in the Statistics by Suite table in outputs. --tagstatinclude <tag>   Includes only these tags in the Statistics by Tag table. --tagstatexclude <tag>   Excludes these tags from the Statistics by Tag table. --tagstatcombine <tags:title>   Creates combined statistics based on tags . --tagdoc <pattern:doc>   Adds documentation to the specified tags . --tagstatlink <pattern:link:title>   Adds external links to the Statistics by Tag table. --expandkeywords <name:pattern|tag:pattern>   Automatically expand keywords in the generated log file. --removekeywords <all|passed|name:pattern|tag:pattern|for|while|wuks>   Removes keyword data from the generated log file. --flattenkeywords <for|while|iteration|name:pattern|tag:pattern>   Flattens keywords in the generated log file. --listener <name:args>   Sets a listener for monitoring test execution. --nostatusrc Sets the return code to zero regardless of failures in test cases. Error codes are returned normally. --runemptysuite   Executes tests also if the selected test suites are empty . --dryrun In the dry run mode tests are run without executing keywords originating from test libraries. Useful for validating test data syntax. -X , --exitonfailure   Stops test execution if any test fails. --exitonerror Stops test execution if any error occurs when parsing test data, importing libraries, and so on. --skipteardownonexit   Skips teardowns if test execution is prematurely stopped. --prerunmodifier <name:args>   Activate programmatic modification of test data . --prerebotmodifier <name:args>   Activate programmatic modification of results . --randomize <all|suites|tests|none>   Randomizes test execution order. --console <verbose|dotted|quiet|none>   Console output type . --dotted Shortcut for --console dotted . --quiet Shortcut for --console quiet . -W , --consolewidth <width>   Sets the width of the console output. -C , --consolecolors <auto|on|ansi|off>   Specifies are colors used on the console. --consolelinks <auto|off>   Controls making paths to results files hyperlinks . -K , --consolemarkers <auto|on|off>   Show markers on the console when top level keywords in a test case end. -P , --pythonpath <path>   Additional locations to add to the module search path . -A , --argumentfile <path>   A text file to read more arguments from. -h , --help Prints usage instructions . --version Prints the version information .
--rpa Turn on generic automation mode. -R , --merge Changes result combining behavior to merging . -N , --name <name>   Sets the name of the top level test suite. -D , --doc <document>   Sets the documentation of the top-level test suite. -M , --metadata <name:value>   Sets free metadata for the top-level test suite. -G , --settag <tag>   Sets the tag(s) to all processed test cases. -t , --test <name>   Selects the test cases by name . --task <name> Alias for --test . -s , --suite <name>   Selects the test suites by name. -i , --include <tag>   Selects the test cases by tag. -e , --exclude <tag>   Selects the test cases by tag. -d , --outputdir <dir>   Defines where to create output files . -o , --output <file>   Sets the path to the generated output file . --legacyoutput Creates output file in Robot Framework 6.x compatible format . -l , --log <file>   Sets the path to the generated log file . -r , --report <file>   Sets the path to the generated report file . -x , --xunit <file>   Sets the path to the generated xUnit compatible result file . -T , --timestampoutputs   Adds a timestamp to output files listed above. --splitlog Split log file into smaller pieces that open in browser transparently. --logtitle <title>   Sets a title for the generated test log. --reporttitle <title>   Sets a title for the generated test report. --reportbackground <colors>   Sets background colors of the generated report. -L , --loglevel <level>   Sets the threshold level to select log messages. Optionally the default visible log level can be given separated with a colon (:). --suitestatlevel <level>   Defines how many levels to show in the Statistics by Suite table in outputs. --tagstatinclude <tag>   Includes only these tags in the Statistics by Tag table. --tagstatexclude <tag>   Excludes these tags from the Statistics by Tag table. --tagstatcombine <tags:title>   Creates combined statistics based on tags . --tagdoc <pattern:doc>   Adds documentation to the specified tags . --tagstatlink <pattern:link:title>   Adds external links to the Statistics by Tag table. --expandkeywords <name:pattern|tag:pattern>   Automatically expand keywords in the generated log file. --removekeywords <all|passed|name:pattern|tag:pattern|for|wuks>   Removes keyword data from the generated outputs. --flattenkeywords <for|foritem|name:pattern|tag:pattern>   Flattens keywords in the generated outputs. --starttime <timestamp>   Sets the starting time of test execution when creating reports. --endtime <timestamp>   Sets the ending time of test execution when creating reports. --nostatusrc Sets the return code to zero regardless of failures in test cases. Error codes are returned normally. --processemptysuite   Processes output files even if files contain empty test suites . --prerebotmodifier <name:args>   Activate programmatic modification of results . -C , --consolecolors <auto|on|ansi|off>   Specifies are colors used on the console. --consolelinks <auto|off>   Controls making paths to results files hyperlinks . -P , --pythonpath <path>   Additional locations to add to the module search path . -A , --argumentfile <path>   A text file to read more arguments from. -h , --help Prints usage instructions . --version Prints the version information .

Robot Framework supports translating section headers , settings , Given/When/Then prefixes used in Behavior Driven Development (BDD) as well as true and false strings used in automatic Boolean argument conversion. This appendix lists all translations for all languages, excluding English, that Robot Framework supports out-of-the-box.

How to actually activate translations is explained in the Localization section. That section also explains how to create custom language definitions and how to contribute new translations.

Link with text content

Link with image content, image with title text.

When documenting test suites, test cases and user keywords or adding metadata to test suites, newlines can be added manually using \n escape sequence .

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 :

Unlike elsewhere in Robot Framework data, leading spaces and consecutive internal spaces are preserved in documentation and metadata. This makes it possible, for example, to split list items to multiple rows and have preformatted text with spaces:

Preserving spaces in documentation and metadata is new in Robot Framework 6.1. With earlier versions spaces need to be escaped with a backslash.

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.

For example, the following test suite or resource file documentation:

will be formatted in HTML as:

First paragraph has only one line.

Second paragraph, this time created with multiple lines.

The documentation syntax supports inline styles bold , italic and code . Bold text can be created by having an asterisk before and after the selected word or words, for example *this is bold* . Italic style works similarly, but the special character to use is an underscore, for example, _italic_ . It is also possible to have bold italic with the syntax _*bold italic*_ .

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.

Inline style examples
Unformatted Formatted
*bold*
_italic_
_*bold italic*_
``code``
*bold*, then _italic_ and finally ``some code`` , then and finally
This is *bold\n
on multiple\n
lines*.
This is

.

All strings that look like URLs are automatically converted into clickable links. Additionally, URLs that end with extension .jpg , .jpeg , .png , .gif , .bmp or .svg (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.

.svg image support is new in Robot Framework 3.2.

It is possible to create custom links and embed images using special syntax [link|content] . This creates a link or image depending are link and content images. They are considered images if they have the same image extensions that are special with URLs or start with data:image/ . The surrounding square brackets and the pipe character between the parts are mandatory in all cases.

Support for the data:image/ prefix is new in Robot Framework 3.2.

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:

If documentation gets longer, it is often a good idea to split it into sections. It is possible to separate sections with titles using syntax = My Title = , where the number of equal signs denotes the level of the title:

Notice that only three title levels are supported and that spaces between equal signs and the title text are mandatory.

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:

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:

ABC
Helloworld
Hi

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:

The above documentation is formatted like this in HTML:

  • a list item
  • second list item is continued

This is outside the list.

It is possible to embed blocks of preformatted text in the documentation. Preformatted block is created by starting lines with '| ', one space being mandatory after the pipe character except on otherwise empty lines. The starting '| ' sequence will be removed from the resulting HTML, but all other whitespace is preserved.

In the following documentation, the two middle lines form a preformatted block when converted to HTML:

The above documentation is formatted like this:

Doc before block:

After block.

Horizontal rulers (the <hr> tag) make it possible to separate larger sections from each others, and they can be created by having three or more hyphens alone on a line:

Some text here.

More text...

Robot Framework has its own time format that is both flexible to use and easy to understand. It is used by several keywords (for example, BuiltIn keywords Sleep and Wait Until Keyword Succeeds ), DateTime library, and timeouts .

The time can always be given as a plain number, in which case it is interpreted to be seconds. Both integers and floating point numbers work, and it is possible to use either real numbers or strings containing numerical values.

In some contexts plain numbers can be interpreted otherwise as times. For example, with WHILE loop limit integers denote the maximum iteration count.

Representing the time as a time string means using a format such as 2 minutes 42 seconds , which is normally easier to understand than just having the value as seconds. It is, for example, not so easy to understand how long a time 4200 is in seconds, but 1 hour 10 minutes is clear immediately.

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:

  • weeks, week, w
  • days, day, d
  • hours, hour, h
  • minutes, minute, mins, min, m
  • seconds, second, secs, sec, s
  • milliseconds, millisecond, millis, ms
  • microseconds, microsecond, us, μs
  • nanoseconds, nanosecond, ns

Support for micro and nanoseconds is new in Robot Framework 6.0. Support for weeks is new in Robot Framework 7.1.

Time can also be given in timer like format hh:mm:ss.mil . In this format both hour and millisecond parts are optional, leading and trailing zeros can be left out when they are not meaningful, and negative times can be represented by adding the - prefix. For example, following timer and time string values are identical:

Timer and time string examples
Timer Time string
00:00:01 1 second
01:02:03 1 hour 2 minutes 3 seconds
1:00:00 1 hour
100:00:00 100 hours
00:02 2 seconds
42:00 42 minutes
00:01:02.003 1 minute 2 seconds 3 milliseconds
00:01.5 1.5 seconds
-01:02.345 - 1 minute 2 seconds 345 milliseconds

Many keywords in Robot Framework standard libraries accept arguments that are handled as Boolean values true or false. If such an argument is given as a string, it is considered false if it is an empty string or equal to FALSE , NONE , NO , OFF or 0 , case-insensitively. Other strings are considered true unless the keyword documentation explicitly states otherwise, and other argument types are tested using the same rules as in Python .

Considering OFF and 0 false is new in Robot Framework 3.1.

This appendix explains how expressions are evaluated using Python in different contexts and how variables in expressions are handled.

Normal ${variable} syntax

Special $variable syntax.

Constructs such as IF/ELSE structures , WHILE loops and inline Python evaluation as well as several BuiltIn keywords accept an expression that is evaluated in Python:

Notice that instead of creating complicated expressions, it is often better to move the logic into a test library . That typically eases maintenance and also enhances execution speed.

Expressions are evaluated using Python's eval function so that normal Python constructs like '${x}' == 'expected' , ${x} > 0 and '${x}'.upper() not in ('FAIL', 'BAD') can be used and all builtin functions like len() and int() are available. In addition to that, all unrecognized Python variables are considered to be modules that are automatically imported. It is possible to use all available Python modules, including the standard modules and the installed third party modules.

The following examples demonstrate using Python builtins as well as modules using the inline Python evaluation syntax, but same expressions would also work with IF/ELSE structures and BuiltIn keywords without the need to use the ${{}} decoration around the expression:

A limitation of using modules is that nested modules like rootmod.submod can only be used if the root module automatically imports the submodule. That is not always the case and using such modules is not possible. An concrete example that is relevant in the automation context is the selenium module that is implemented, at least at the time of this writing, so that just importing selenium does not import the selenium.webdriver submodule. Another limitation is that modules cannot be used in the expression part of a list comprehension when using Python 3. A workaround to both of these problems is using the BuiltIn keyword Evaluate that accepts modules to be imported and added to the evaluation namespace as an argument:

The Evaluate keyword also supports custom evaluation namespaces if further customization is needed. See its documentation in the BuiltIn library for more details.

When a variable is used in the expression using the normal ${variable} syntax, its value is replaced before the expression is evaluated. This means that the value used in the expression will be the string representation of the variable value, not the variable value itself. This is not a problem with numbers and other objects that have a string representation that can be evaluated directly. For example, if we have a return code as an integer in variable ${rc} , using something like ${rc} > 0 is fine.

With other objects the behavior depends on the string representation. Most importantly, strings must always be quoted either with single or double quotes like '${x}' , and if they can contain newlines, they must be triple-quoted like '''${x}''' . Strings containing quotes themselves cause additional problems, but triple-quoting typically handles them. Also the backslash character \ is problematic, but can be handled by using Python's raw-string notation like r'${path}' .

Quoting strings is not that convenient, but there are cases where replacing the variable with its string representation causes even bigger problems. For example, if the variable value can be either a string or Python None , quoting like '${var}' is needed because otherwise strings do not work, but then None is interpreted to be a string as well. Luckily there is an easy solution to these problems discussed in this section.

Actual variables values are available in the evaluation namespace and can be accessed using special variable syntax without the curly braces like $variable . Such variables should never be quoted, not even if they contain strings.

Compare this these examples with the example in the previous section:

Using the $variable syntax slows down expression evaluation a little. This should not typically matter, but should be taken into account if complex expressions are evaluated often and there are strict time constrains. Moving such logic to test libraries is typically a good idea anyway.

Due to technical reasons, these special variables are available during evaluation as local variables. That makes them unavailable in non-local scopes such as in the expression part of list comprehensions and inside lambdas.

This appendix lists file extensions, media types, and so on, that are associated with Robot Framework.

Suite files with the following extensions are parsed automatically:

Using other extensions is possible, but it requires separate configuration .

Resource files can use the following extensions:

The media type to use with Robot Framework data is text/robotframework .

The default remote server port is 8270. The port has been registered by IANA .

IMAGES

  1. Invalid environment assignment · Issue #462 · ArkCase/arkcase-ce · GitHub

    ignoring invalid environment assignment systemd

  2. Как удалить службу Systemd

    ignoring invalid environment assignment systemd

  3. Systemd: Systemd Service Failing with exit-code status=203/EXEC

    ignoring invalid environment assignment systemd

  4. pip install出现WARNING: Ignoring invalid distribution的解决方案_warning

    ignoring invalid environment assignment systemd

  5. [Solved] Systemd error : Executable path is not absolute

    ignoring invalid environment assignment systemd

  6. fix WARNING Ignoring invalid distribution site-packages

    ignoring invalid environment assignment systemd

VIDEO

  1. Understanding business Environment Assignment 3

  2. Why Some Linux Users Dislike SystemD? #linux #systemd

  3. RMIT Understanding the Business Environment

  4. How to fully remove old [invalid] virtual enviroments from PyCharm

  5. CARE FOR ENVIRONMENT ASSIGNMENT FOR BED 2ND SEMESTER #bed #2ndsemester #assignment #assigment #2024

  6. Understanding Business Environment Assignment 3 for ACE

COMMENTS

  1. How to specify an Environment systemd directive containing

    EDIT. I found the documentation on observed environmental variable behavior, thanks to another answer:. Basic environment variable substitution is supported. Use "${FOO}" as part of a word, or as a word of its own, on the command line, in which case it will be erased and replaced by the exact value of the environment variable (if any) including all whitespace it contains, always resulting in ...

  2. systemd logs "Ignoring invalid environment assignment" for OVS services

    systemd logs "Ignoring invalid environment assignment" for OVS services when --mlockall=no is used Solution Verified - Updated 2024-06-03T16:15:26+00:00 - English

  3. How to set environment variable in systemd service?

    No, no, no. /etc/sysconfig is not recomended. It is discouraged, along with /etc/default/* from debian, because they are pointless, and the names are meaningless and make sense only for backwards compatibility reasons (all of /etc is about configuration of the system, not just /etc/sysconfig, and /etc/defaults is for overrides, not the defaults).

  4. systemd

    Nov 26 16:47:50 vps203756 systemctl[14275]: Failed to set environment: Invalid environment assignments Nov 26 16:47:50 vps203756 systemd[1]: jetty.service: Control process exited, code=exited status=1 Nov 26 16:47:50 vps203756 systemd[1]: jetty.service: Failed with result 'exit-code'. ... Escaping on systemd ExecStart= can become complex and ...

  5. Systemd Environment and EnvironmentFile not working

    Environment and EnvironmentFile set the variables, usable by the unit, but like the sh command, does not export it to child processes. For that, you also need to list it in PassEnvironment, just as you would with the export shell command. See the systemd documentation on EnvironmentFile and PassEnvironment. Also, note that the contents of an EnvironmentFile is not a shell script, but key-value ...

  6. How to set environmental variable in systemd service

    Stack Exchange Network. Stack Exchange network consists of 183 Q&A communities including Stack Overflow, the largest, most trusted online community for developers to learn, share their knowledge, and build their careers.. Visit Stack Exchange

  7. systemd script: Assignment outside of section; Missing

    I have double-checked that the anonymization didn't change anything. The lines that were changed were the Description, Requires, After, ExecStart, and ExecStop lines, as well as the filename of the *.service file.

  8. systemctl --user import-environment fails #15179

    It sends to systemd full current environment as inherited by systemctl. The possible reason for these errors is. "invalid" variable name (hint - do you have any exported function?) value is not UTF-8. value contains TAB or LF character (which is yet another reason to reject exported functions) value too large (SC_ARG_MAX) You should be able to ...

  9. Segfault inside _reset_errno_ after "Ignoring invalid environment

    Submission type Bug report systemd version the issue has been seen with Yes, I've read the "do not submit bug reports about anything but the two most recently released", but since I can reproduce it on this one box only, I cannot tell wh...

  10. Starting a systemd service at boot?

    I fixed this by: 1: creating lwsmd.service in /lib/systemd/system (not in /etc/systemd/system) 2: making a symlink from /lib/systemd/system to /etc/systemd.system. 3: enabling the service with ...

  11. How do I use environment variables from file in systemd

    Stack Exchange Network. Stack Exchange network consists of 183 Q&A communities including Stack Overflow, the largest, most trusted online community for developers to learn, share their knowledge, and build their careers.. Visit Stack Exchange

  12. 1763195

    1763195 - Invalid environment assignment, ignoring: MDADM_CHECK_DURATION='"6 hours"'. RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal.

  13. How do I override or configure systemd services?

    @MarkEdington from the systemd.service(5) manpage, section on ExecStart: "Unless Type= is oneshot, exactly one command must be given.When Type=oneshot is used, zero or more commands may be specified. Commands may be specified by providing multiple command lines in the same directive, or alternatively, this directive may be specified more than once with the same effect.

  14. [/usr/lib/systemd/system/mdcheck_xxx.service:14] Invalid environment

    A Red Hat subscription provides unlimited access to our knowledgebase, tools, and much more.

  15. linux

    This is the expected behavior with systemd. To understand what the environment that systemd services run in, you can refer to man systemd.exec, specifically the section ENVIRONMENT VARIABLES IN SPAWNED PROCESSES. As it says, only very few variables are set, and you have to set anything else yourself.

  16. Inconsistent and ad hoc quoting rules in Environment= and

    Assignments via Environment= in unit files and the assignments in EnvironmentFile= are two very different things. The format of unit files (i.e. a format originating in Windows INI files) and of environment files (i.e. a format based on shell scripts, but drastically reduced in feature set) are very different from each other, and you shouldn't assume that what is OK in one is OK in the other.

  17. Using environment variables in systemd units

    It takes a space-separated list of variable assignments. This option may be specified more than once in which case all listed variables will be set. If the same variable is set twice, the later setting will override the earlier setting. If the empty string is assigned to this option, the list of environment variables is reset, all prior ...

  18. systemctl .service can't read the environment variable

    Because systemd is not running in a shell with your user and that user environment and those variables. when i running uwsgi in command line then you are using your own user with that environment and those variables.

  19. How do I use environment variables when running as service?

    I am using Logstash v5.1.0 on Ubuntu 16.04 and my logstash conf file includes an environment variable reference to set the output graphite server, but I cannot get it to work when running as a service. My config file contains (missing a few lines for clarity): graphite { host => "${GRAPHITE_SERVER}" } and my /etc/environment file contains: GRAPHITE_SERVER=54.40.235.208 Yet with configuration ...

  20. 1774354

    RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com).If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal.If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here.Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST ...

  21. Systemd ignores system-wide environment variables #106

    the behavior is not consistent. With /etc/environment existing. a. ssh server <- variables are not visible in user's shell. b. sudo -s <- variables are visible in root's shell. c. su - anotheruser <- variables are not visible in anotheruser's shell. I'll try to export things in /etc/profile next. Author.

  22. Robot Framework User Guide

    The PATH environment variable lists directories where commands executed in a system are searched from. To make using Python, pip and Robot Framework easier from the command line, it is recommended to add the Python installation directory as well as the directory where commands like pip and robot are installed into PATH .