Robot Framework supports numerous libraries which can be used out-of-the-box. But what if we need functionality which is custom to our application? In that case, we can write our own custom library. In this post, we will take a look at how we can share keywords between Test Suites and how we can create our own custom Robot Framework library.

1. Introduction

In our previous posts Automated Acceptance Testing With Robot Framework and How to Write Data Driven Tests With Robot Framework, we learned the basics of Robot Framework. We also learned how to write keywords in order to reuse commonly used functionality between test cases. All of this remained in one Test Suite, but what if you have created keywords which you also want to use in other Test Suites? We will explain how to make use of resource files and how to create custom Robot Framework Python libraries in order to share common keywords between Test Suites.

We will continue using the examples of our previous posts, it is therefore advised to take a look at those posts but it is not required if you already have knowledge of the basic Robot Framework concepts. The source code can be found at GitHub.

2. Share Keywords With Resource Files

In order to share keywords between Test Suites, we can make use of resource files. We will start from the demo application we used in our previous posts and transform the Robot Framework test script in order to make use of a resource file. For the demo application (the application under test), we created an employee.py Python script which allows us to add an employee to a CSV file, to retrieve the list of employees and to remove the entire list of employees. We also created a Robot Framework test script employee.robot for this.

First of all, we are going to create the resource file with all the keywords of the employee.robot file. We copy all the keywords from the employee.robot file to an employee.resource file which is located in the data directory. A resource file consists of an identical structure as a test script, but without the Test Cases section and Test Suite/Test Case related items like Setup, Teardown, etc.

| *** Settings ***   |
| Documentation      | Resource for Employee interactions
| Library            | OperatingSystem

| *** Variables ***  |
| ${APPLICATION}     | python3 ../employee.py

| *** Keywords ***              |
| Clear Employees List          | [Documentation] | Clears the list of employees
| | ${rc}                       | ${output} =     | Run and Return RC and Output | ${APPLICATION} remove_all_employees
| | Should Be Equal As Integers | ${rc}           | 0

| Retrieve Employees List       | [Documentation] | Return the list of employees
| | ${rc}                       | ${output} =     | Run and Return RC and Output | ${APPLICATION} list_employees
| | Should Be Equal As Integers | ${rc}           | 0
| | [Return]                    | ${output}

| Add Employee                  | [Documentation] | Add an employee to the list of employees
|                               | [Arguments]     | ${first_name}
|                               | ...             | ${last_name}
| | ${rc}                       | ${output} =     | Run and Return RC and Output | ${APPLICATION} add_employee ${first_name} ${last_name}
| | Should Be Equal As Integers | ${rc}           | 0

In a next step, we copy the contents of the employee.robot file to a resource_example.robot file in order to make the necessary changes to the test script for using the resource file:

  • We import the resource file in the Settings section by means of the Resource keyword
  • We remove all the other keyword related items which have been copied to the resource file like the Variables section and the Keywords section.
| *** Settings ***   |
| Documentation      | Test the employee Python script
| Library            | OperatingSystem
| Resource           | data/employee.resource

| *** Test Cases ***            |                 |
| Empty Employees List          | [Documentation] | Verify the output of an empty employees list
| | [Setup]                     | Clear Employees List
| | ${output} =                 | Retrieve Employees List
| | Should Be Equal             | ${output}       | []

| Add Employee                  | [Documentation] | Verify adding an employee
| | [Setup]                     | Clear Employees List
| | Add Employee                | first_name=John | last_name=Doe
| | ${output} =                 | Retrieve Employees List
| | Should Be Equal             | ${output}       | ['John Doe']
| | [Teardown]                  | Clear Employees List

Execution of the test is identical as before.

$ robot resource_example.robot

From the test execution point of view, nothing has changed. It is merely an amelioration for the maintainability of your Robot Framework test scripts and for re-usability of keywords which have been developed by your fellow colleagues. It is therefore also important to generate documentation for the resource files in order to be able to search for existing keywords more easily. This can be done by means of libdoc:

$ python3 -m robot.libdoc -f html employee.resource employee.html

Several options are available for libdoc, we only specify the output format to html by means of the -f option, followed by the source and target file.

The employee.html file is the following:

robotframework-resource-doc

It is important to provide good documentation and examples. This will make it easier to understand the purpose of a keyword and how to use it. It is clear that the documentation in this example needs some attention, we will do a better job when creating the custom library in the next section of this post.

Just as we can generate documentation for our resource files, so we can generate documentation for our Test Suites. We can use the testdoc utility for that purpose.

$ python3 -m robot.testdoc employee.robot employee.html

Again, this creates an html file for us with the generated test documentation:

robotframework-testdoc

3. Share Keywords With Custom Libraries

Another approach for sharing keywords, is by developing them in a custom Python library. This requires more developer skills than creating keywords in a Test Suite or resource file. Again, we copy the contents of the employee.robot file, this time to a custom_library_example.robot file in order to make the changes for using a custom Python library for the keywords. The Python library is located in the library/employee_lib.py file.

First of all, we are going to remove the Clear Employees List keyword from the custom_library_example.robot file and replace the keyword with its counterpart in the library file. We import the library file by means of the Library keyword in the Settings section and add a variable APPLICATION_PATH containing the path to our application. We will use this variable as an argument for the new library keyword. The test script is changed as follows:

| *** Settings ***   |
| Documentation      | Test the employee Python script
| Library            | OperatingSystem
| Library            | library/employee_lib.py

| *** Variables ***   |
| ${APPLICATION}      | python3 ../employee.py
| ${APPLICATION_PATH} | ../employee.py

...
| | [Setup]           | Clear Employees List | ${APPLICATION_PATH}
...

The employee_lib.py is just a regular Python script. The keyword Clear Employees List is defined as function clear_employees_list. The keyword can be used in the test script without the underscores. In the function, we just execute the application. There are other and probably better ways for invoking a Python script, but we want to keep it simple and straightforward in this example. The argument Check=True of the subprocess will ensure that an exception is thrown when the underlying process fails. In its turn, this will ensure that the Robot Framework test will fail, the exception will be propagated to the test case execution and the test will fail. And take a look at it: it is just plain Python code!

from robot.api.deco import keyword
import subprocess

ROBOT_AUTO_KEYWORDS = False

@keyword
def clear_employees_list(application_path):
    subprocess.run(['python3', application_path, 'remove_all_employees'], check=True)

Also notice that we have set the variable ROBOT_AUTO_KEYWORDS to False. This will ensure that only functions annotated with the @keyword annotation will be available as a keyword in the test script. Otherwise, all functions will be visible from within the test script and we do not want that all of the time (e.g. a helper function which is only intended to be used within the Python library). It is also possible to make use of classes, but we refer to the official Robot Framework documentation for more information on that topic.

Now execute the test:

$ robot custom_library_example.robot 
==============================================================================
Custom Library Example :: Test the employee Python script 
==============================================================================
Empty Employees List :: Verify the output of an empty employees list The file does not exist
Empty Employees List :: Verify the output of an empty employees list  | PASS |
------------------------------------------------------------------------------
Add Employee :: Verify adding an employee The file does not exist
Add Employee :: Verify adding an employee                             | PASS |
------------------------------------------------------------------------------
Custom Library Example :: Test the employee Python script             | PASS |
2 critical tests, 2 passed, 0 failed
2 tests total, 2 passed, 0 failed

Our test cases pass. The only difference is that we have an extra console output line containing a message ‘The file does not exist’. This is caused by the Setup where we clear the employees list and the file does not exist. The print statement in the application under test employee.py is the originator of this message.

Next, we are going to move the Retrieve Employees List to the Python library. We remove it from the Keywords section from the test script and change the calls to the Retrieve Employees List by adding the APPLICATION_PATH as an argument, just as we did for the Clear Employees List.

| | ${output} =    | Retrieve Employees List | ${APPLICATION_PATH}

The corresponding library keyword is the function retrieve_employees_list. Notice that we do have a return value here. Only the last line of the subprocess output is returned, that is why we needed to strip off the line feed. If we do not strip it off, an empty line is returned.

@keyword
def retrieve_employees_list(application_path):
    process = subprocess.run(['python3', application_path, 'list_employees'], check=True, stdout=subprocess.PIPE, text=True)
    result = process.stdout.rstrip('\n')
    return result

Leaves us to change the Add Employee keyword, which also takes the first_name and last_name as arguments besides the APPLICATION_PATH.

| | Add Employee | ${APPLICATION_PATH} | first_name=John | last_name=Doe

The corresponding library keyword becomes the following:

@keyword
def add_employee(application_path, first_name, last_name):
    subprocess.run(['python3', application_path, 'add_employee', first_name, last_name], check=True)

We also add the variable ROBOT_LIBRARY_VERSION which can be used for versioning the library.

ROBOT_LIBRARY_VERSION = '0.1'

As mentioned before, it is very important to document the keywords with a meaningful description, specifying the arguments and some good examples of how to use the keyword. An example for the Clear Employees List keyword is the following:

@keyword
def clear_employees_list(application_path):
    """
    Clear the list of employees.

    Arguments:
    - ''application_path:''     The relative (or absolute) path where the Employee script resides\n

    Examples:
    | Clear Employees List | ../employee.py
    """
    subprocess.run(['python3', application_path, 'remove_all_employees'], check=True)

Just like we did for the resource files, we can generate the corresponding documentation for the library:

$ python3 -m robot.libdoc -f html employee_lib.py employee_lib.html

The generated documentation is the following and contains some meaningful information this time:

robotframework-python-library-doc

4. Conclusion

Making use of resource files and custom Python libraries is definitely something you need to consider when using Robot Framework. Sharing keywords instead of copying them will make your Robot Framework Test Suites more maintainable and it will get everyone up to speed. You have the choice between resource files and custom Python libraries for doing so. Custom Python libraries have the advantage that you have accessibility to the complete Python language, whereas keywords are more limited in there capabilities. Besides that, creating a Python library requires developer skills whereas keywords are more non-developer friendly.