2.7 Resource and variable files
User keywords and variables in test case files and test suite initialization files can only be used in files where they are created, but resource files provide a mechanism for sharing them. Since the resource file structure is very close to test case files, it is easy to create them.
Variable files provide a powerful mechanism for creating and sharing variables. For example, they allow values other than strings and enable creating variables dynamically. Their flexibility comes from the fact that they are created using Python code, which also makes them somewhat more complicated than Variable tables.
2.7.1 Resource files
Taking resource files into use
Resource files are imported using the Resource setting in the Settings table. The path to the resource file is given in the cell after the setting name.
If the path is given in an absolute format, it is used directly. In other cases, the resource file is first searched relatively to the directory where the importing file is located. If the file is not found there, it is then searched from the directories in PYTHONPATH. The path can contain variables, and it is recommended to use them to make paths system-independent (for example, ${RESOURCES}/login_resources.html or ${RESOURCE_PATH}). Additionally, slashes (/) in the path are automatically changed to backslashes () on Windows.
Importing resource files | ||
---|---|---|
Setting | Value | Value |
Resource | myresources.html | |
Resource | ../data/resources.html | |
Resource | ${RESOURCES}/common.tsv |
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.
Resource file structure
The higher-level structure of resource files is the same as that of test case files otherwise, but, of course, they cannot contain Test Case tables. Additionally, the Setting table in resource files can contain only import settings (Library, Resource, Variables) and Documentation. The Variable table and Keyword table are used exactly the same way as in test case files.
If several resource files have a user keyword with the same name, they must be used so that the keyword name is prefixed with the resource file name without the extension (for example, myresources.Some Keyword and common.Some Keyword). Moreover, if several resource files contain the same variable, the one that is imported first is taken into use.
Documenting resource files
Keywords created in a resource file can be documented using [Documentation] setting. Starting from Robot Framework 2.1 also the resource file itself can have Documentation in the Setting table similarly as test suites.
Both libdoc and RIDE use these documentations, and they are naturally available for anyone opening resource files. The first line of the documentation of a keyword is logged when it is run, but otherwise resource file documentations are ignored during the test execution.
Example resource file
Setting | Value | Value | Value |
---|---|---|---|
Documentation | An example resource file | ||
Library | SeleniumLibrary | ||
Resource | ${RESOURCES}/common.html |
Variable | Value | Value | Value |
---|---|---|---|
${HOST} | localhost:7272 | ||
${LOGIN_URL} | http://${HOST}/ | ||
${WELCOME_URL} | http://${HOST}/welcome.html | ||
${BROWSER} | Firefox |
Keyword | Action | Argument | Argument | Argument |
---|---|---|---|---|
Open Login Page | [Documentation] | Opens browser | to login page | |
Open Browser | ${LOGIN_URL} | ${BROWSER} | ||
Title Should Be | Login Page | |||
Input Name | [Arguments] | ${name} | ||
Input Text | username_field | ${name} | ||
Input Password | [Arguments] | ${password} | ||
Input Text | password_field | ${password} |
2.7.2 Variable files
Variable files contain variables that can be used in the test data. Variables can also be created using variable tables or set from the command line, but variable files allow creating them dynamically and their variables can contain any objects.
Variable files are typically implemented as Python modules and there are two different approaches for creating variables:
Variables are specified as module attributes. In simple cases, the syntax is so simple that no real programming is needed. For example, MY_VAR = 'my value' creates a variable ${MY_VAR} with the specified text as the value.
Getting variables from a special function
Variable files can have a special get_variables (or getVariables) method that returns variables as a mapping. Because the method can take arguments this approach is very flexible.
Alternatively variable files can be implemented as Python or Java classes that the framework will instantiate. Also in this case it is possible to create variables as attributes or get them from a special method.
Taking variable files into use
Setting table
All test data files can import variables using the Variables setting in the Setting table, in the same way as resource files are imported using the Resource setting. Similarly to resource files, the path to the imported variable file is considered relative to the directory where the importing file is, and if not found, it is searched from the directories in PYTHONPATH. The path can also contain variables, and slashes are converted to backslashes on Windows. If an argument file takes arguments, they are specified in the cells after the path and also they can contain variables.
Importing a variable file | |||
---|---|---|---|
Setting | Value | Value | Value |
Variables | myvariables.py | ||
Variables | ../data/variables.py | ||
Variables | ${RESOURCES}/common.py | ||
Variables | taking_arguments.py | arg1 | ${ARG2} |
All variables from a variable file are available in the test data file that imports it. If several variable files are imported and they contain a variable with the same name, the one in the earliest imported file is taken into use. Additionally, variables created in Variable tables and set from the command line override variables from variable files.
Command line
Another way to take variable files into use is using the command line option --variablefile. Variable files are referenced using a path to them, and possible arguments are joined to the path with a colon (:):
--variablefile myvariables.py
--variablefile path/variables.py
--variablefile /absolute/path/common.py
--variablefile taking_arguments.py:arg1:arg2
Starting from Robot Framework 2.8.2, variable files taken into use from the command line are also searched from the PYTHONPATH similarly as variable files imported in the Setting table.
If a variable file is given as an absolute Windows path, the colon after the drive letter is not considered a separator:
--variablefile C:\path\variables.py
Starting from Robot Framework 2.8.7, it is also possible to use a semicolon (;) as an argument separator. This is useful if variable file arguments themselves contain colons, but requires surrounding the whole value with quotes on UNIX-like operating systems:
--variablefile "myvariables.py;argument:with:colons"
--variablefile C:\path\variables.py;D:\data.xls
Variables in these variable files are globally available in all test data files, similarly as individual variables set with the --variable option. If both --variablefile and --variable options are used and there are variables with same names, those that are set individually with --variable option take precedence.
Creating variables directly
Basic syntax
When variable files are taken into use, they are imported as Python modules and all their global attributes that do not start with an underscore (_) are considered to be variables. Because variable names are case-insensitive, both lower- and upper-case names are possible, but in general, capital letters are recommended for global variables and attributes.
VARIABLE = "An example string"
ANOTHER_VARIABLE = "This is pretty easy!"
INTEGER = 42
STRINGS = ["one", "two", "kolme", "four"]
NUMBERS = [1, INTEGER, 3.14]
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 and the last two are lists. All these variables are scalar variables, even the ones containing lists as values. To create list variables, the variable name must be prefixed with LIST__ (note the two underscores).
LIST__STRINGS = ["list", "of", "strings"]
LIST__MIXED = ["first value", -1.1, None, True]
The variables in both the examples above could be created also using the Variable table below.
Variable | Value | Value | Value | Value |
---|---|---|---|---|
${VARIABLE} | An example string | |||
${ANOTHER_VARIABLE} | This is pretty easy! | |||
${INTEGER} | ${42} | |||
${STRINGS} | one | two | kolme | four |
${NUMBERS} | ${1} | ${INTEGER} | ${3.14} | |
@{STRINGS} | list | of | strings | |
@{MIXED} | first value | ${-1.1} | ${None} | ${True} |
Note
Variables are not replaced in strings got from variable files. For example, VAR = "an ${example}" would create variable ${VAR} with a literal string value an ${example} regardless would variable ${example} exist or not.
Using objects as values
Variables in variable files are not limited to having only strings or other base types as values like variable tables. Instead, their variables can contain any objects. In the example below, the variable ${MAPPING} contains a Java Hashtable with two values (this example works only when running tests on Jython).
from java.util import Hashtable
MAPPING = Hashtable()
MAPPING.put("one", 1)
MAPPING.put("two", 2)
The second example creates ${MAPPING} as a Python dictionary and also has two variables created from a custom object implemented in the same file.
MAPPING = {'one': 1, 'two': 2}
class MyObject:
def init(self, name):
self.name = name
OBJ1 = MyObject('John')
OBJ2 = MyObject('Jane')
Creating variables dynamically
Because variable files are created using a real programming language, they can have dynamic logic for setting variables.
import os
import random
import time
USER = os.getlogin() # current login name
RANDOMINT = random.randint(0, 10) # random integer in range [0,10]_
CURRENTTIME = time.asctime() # timestamp like 'Thu Apr 6 12:45:21 2006'_
if time.localtime()[3] > 12:
AFTERNOON = True
else:
AFTERNOON = False
The example above uses standard Python libraries to set different variables, but you can use your own code to construct the values. The example below illustrates the concept, but similarly, your code could read the data from a database, from an external file or even ask it from the user.
import math
def get_area(diameter):
radius = diameter / 2
area = math.pi radius radius
return area
AREA1 = get_area(1)
AREA2 = get_area(2)
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:
import math as _math
def _get_area(diameter):
radius = diameter / 2.0
area = _math.pi radius radius
return area
AREA1 = _get_area(1)
AREA2 = _get_area(2)
If there is a large number of other attributes, instead of prefixing them all, it is often easier to use a special attribute all and give it a list of attribute names to be processed as variables.
import math
all = ['AREA1', 'AREA2']
def get_area(diameter):
radius = diameter / 2.0
area = math.pi radius radius
return area
AREA1 = get_area(1)
AREA2 = get_area(2)
Note
The all attribute is also, and originally, used by Python to decide which attributes to import when using the syntax from modulename import *.
Getting variables from a special function
An alternative approach for getting variables is having a special getvariables function (also camelCase syntax getVariables is possible) in a variable file. If such a function exists, Robot Framework calls it and expects to receive variables as a Python dictionary or a Java Map with variable names as keys and variable values as values. Variables are considered to be scalars, unless prefixed with LIST_, and values can contain anything. The example below is functionally identical to the first examples of creating variables directly above.
def get_variables():
variables = {"VARIABLE ": "An example string",
"ANOTHER_VARIABLE": "This is pretty easy!",
"INTEGER": 42,
"STRINGS": ["one", "two", "kolme", "four"],
"NUMBERS": [1, 42, 3.14],
"LIST__STRINGS": ["list", "of", "strings"],
"LIST__MIXED": ["first value", -1.1, None, True]}
return variables
get_variables can also take arguments, which facilitates changing what variables actually are created. Arguments to the function are set just as any other arguments for a Python function. When taking variable files into use in the test data, arguments are specified in cells after the path to the variable file, and in the command line they are separated from the path with a colon.
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.
variables1 = {'scalar': 'Scalar variable',
'LIST__list': ['List','variable']}
variables2 = {'scalar' : 'Some other value',
'LIST__list': ['Some','other','value'],
'extra': 'variables1 does not have this at all'}
def get_variables(arg):
if arg == 'one':
return variables1
else:
return variables2
Implementing variable file as Python or Java class
Starting from Robot Framework 2.7, it is possible to implement variables files as Python or Java classes.
Implementation
Because variable files are always imported using a file system path, creating them as classes has some restrictions:
- Python classes must have the same name as the module they are located.
- Java classes must live in the default package.
- Paths to Java classes must end with either .java or .class. The class file must exists in both cases.
Regardless the implementation language, the framework will create an instance of the class using no arguments and variables will be gotten from the instance. Similarly as with modules, variables can be defined as attributes directly in the instance or gotten from a special get_variables (orgetVariables) method.
When variables are defined directly in an instance, all attributes containing callable values are ignored to avoid creating variables from possible methods the instance has. If you would actually need callable variables, you need to use other approaches to create variable files.
Examples
The first examples create variables from attributes using both Python and Java. Both of them create variables ${VARIABLE} and @{LIST} from class attributes and ${ANOTHER VARIABLE} from an instance attribute.
class StaticPythonExample(object):
variable = 'value'
LIST__list = [1, 2, 3]
_not_variable = 'starts with an underscore'
def init(self):
self.another_variable = 'another value'
public class StaticJavaExample {
public static String variable = "value";
public static String[] LIST__list = {1, 2, 3};
private String notVariable = "is private";
public String anotherVariable;
public StaticJavaExample() {
anotherVariable = "another value";
}
}
The second examples utilizes dynamic approach for getting variables. Both of them create only one variable ${DYNAMIC VARIABLE}.
class DynamicPythonExample(object):
def get_variables(self, *args):
return {'dynamic variable': ' '.join(args)}
import java.util.Map;
import java.util.HashMap;
public class DynamicJavaExample {
public Map<String, String> getVariables(String arg1, String arg2) {
HashMap<String, String> variables = new HashMap<String, String>();
variables.put("dynamic variable", arg1 + " " + arg2);
return variables;
}
}