Skip to content

Latest commit

 

History

History
823 lines (644 loc) · 30.7 KB

File metadata and controls

823 lines (644 loc) · 30.7 KB

Creating user keywords

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

In many ways, the overall user keyword syntax is identical to the `test case syntax`_. User keywords are created in keyword tables which differ from test case tables only by the name that is used to identify them. User keyword names are in the first column similarly as test cases names. Also user keywords are created from keywords, either from keywords in test libraries or other user keywords. Keyword names are normally in the second column, but when setting variables from keyword return values, they are in the subsequent columns.

*** Keywords ***
Open Login Page
    Open Browser    http://host/login.html
    Title Should Be    Login Page

Title Should Start With
    [Arguments]    ${expected}
    ${title} =    Get Title
    Should Start With    ${title}    ${expected}

Most user keywords take some arguments. This important feature is used already in the second example above, and it is explained in detail later in this section, similarly as user keyword return values.

User keywords can be created in `test case files`_, `resource files`_, and `test suite initialization files`_. Keywords created in resource files are available for files using them, whereas other keywords are only available in the files where they are created.

User keywords can have similar settings as `test cases`__, and they have the same square bracket syntax separating them from keyword names. All available settings are listed below and explained later in this section.

`[Documentation]`:setting:
Used for setting a user keyword documentation.
`[Tags]`:setting:
Sets tags for the keyword.
`[Arguments]`:setting:
Specifies user keyword arguments.
`[Return]`:setting:
Specifies user keyword return values.
`[Teardown]`:setting:
Specify user keyword teardown.
`[Timeout]`:setting:
Sets the possible `user keyword timeout`_. Timeouts_ are discussed in a section of their own.

Note

Setting names are case-insensitive, but the format used above is recommended. 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.

The user keyword name is defined in the first column of the user keyword table. 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 :setting:`[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`_.

*** Keywords ***
One line documentation
    [Documentation]    One line documentation.
    No Operation

Multiline documentation
    [Documentation]    The first line creates the short doc.
    ...
    ...                This is the body of the documentation.
    ...                It is not shown in Libdoc outputs but only
    ...                the short doc is shown in logs.
    No Operation

Short documentation in multiple lines
    [Documentation]    If the short doc gets longer, it can span
    ...                multiple physical lines.
    ...
    ...                The body is separated from the short doc with
    ...                an empty line.
    No Operation

Sometimes keywords need to be removed, replaced with new ones, or deprecated for other reasons. User keywords can be marked deprecated by starting the documentation with *DEPRECATED*, which will cause a warning when the keyword is used. For more information, see the `Deprecating keywords`_ section.

Note

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

Starting from Robot Framework 2.9, keywords can also have tags. User keywords tags can be set with :setting:`[Tags]` setting similarly as `test case tags`_, but possible :setting:`Force Tags` and :setting:`Default Tags` setting do not affect them. Additionally keyword tags can be specified on the last line of the documentation with Tags: prefix and separated by a comma. For example, following two keywords would both get same three tags.

*** Keywords ***
Settings tags using separate setting
    [Tags]    my    fine    tags
    No Operation

Settings tags using documentation
    [Documentation]    I have documentation. And my documentation has tags.
    ...                Tags: my, fine, tags
    No Operation

Keyword tags are shown in logs and in documentation generated by Libdoc_, where the keywords can also be searched based on tags. The `--removekeywords`__ and `--flattenkeywords`__ commandline options also support selecting keywords by tag, and new usages for keywords tags are possibly added in later releases.

Similarly as with `test case tags`_, user keyword tags with robot- and robot: prefixes are reserved__ for special features by Robot Framework itself. Users should thus not use any tag with these prefixes unless actually activating the special functionality.

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 :setting:`[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 :setting:`[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}.

*** Keywords ***
One Argument
    [Arguments]    ${arg_name}
    Log    Got argument ${arg_name}

Three Arguments
    [Arguments]    ${arg1}    ${arg2}    ${arg3}
    Log    1st argument: ${arg1}
    Log    2nd argument: ${arg2}
    Log    3rd argument: ${arg3}

When creating user keywords, positional arguments are sufficient in most situations. It is, however, sometimes useful that keywords have `default values`_ for some or all of their arguments. Also user keywords support default values, and the needed new syntax does not add very much to the already discussed basic syntax.

In short, default values are added to arguments, so that first there is the equals sign (=) and then the value, for example ${arg}=default. There can be many arguments with defaults, but they all must be given after the normal positional arguments. The default value can contain a variable_ created on `test, suite or global scope`__, but local variables of the keyword executor cannot be used. Starting from Robot Framework 3.0, default value can also be defined based on earlier arguments accepted by the keyword.

Note

The syntax for default values is space sensitive. Spaces before the = sign are not allowed, and possible spaces after it are considered part of the default value itself.

*** Keywords ***
One Argument With Default Value
    [Arguments]    ${arg}=default value
    [Documentation]    This keyword takes 0-1 arguments
    Log    Got argument ${arg}

Two Arguments With Defaults
    [Arguments]    ${arg1}=default 1    ${arg2}=${VARIABLE}
    [Documentation]    This keyword takes 0-2 arguments
    Log    1st argument ${arg1}
    Log    2nd argument ${arg2}

One Required And One With Default
    [Arguments]    ${required}    ${optional}=default
    [Documentation]    This keyword takes 1-2 arguments
    Log    Required: ${required}
    Log    Optional: ${optional}

 Default Based On Earlier Argument
    [Arguments]    ${a}    ${b}=${a}    ${c}=${a} and ${b}
    Should Be Equal    ${a}    ${b}
    Should Be Equal    ${c}    ${a} and ${b}

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.

*** Test Cases ***
Example
    Two Arguments With Defaults    arg2=new 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.

*** Keywords ***
Any Number Of Arguments
    [Arguments]    @{varargs}
    Log Many    @{varargs}

One Or More Arguments
    [Arguments]    ${required}    @{rest}
    Log Many    ${required}    @{rest}

Required, Default, Varargs
    [Arguments]    ${req}    ${opt}=42    @{others}
    Log    Required: ${req}
    Log    Optional: ${opt}
    Log    Others:
    FOR    ${item}    IN    @{others}
        Log    ${item}
    END

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:

*** Test Cases ***
Varargs with user keywords
    Any Number Of Arguments
    Any Number Of Arguments    arg
    Any Number Of Arguments    arg1    arg2    arg3   arg4
    One Or More Arguments    required
    One Or More Arguments    arg1    arg2    arg3   arg4
    Required, Default, Varargs    required
    Required, Default, Varargs    required    optional
    Required, Default, Varargs    arg1    arg2    arg3    arg4    arg5

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.

*** Keywords ***
Free Named Only
    [Arguments]    &{named}
    Log Many    &{named}

Positional And Free Named
    [Arguments]    ${required}    &{extra}
    Log Many    ${required}    &{extra}

Run Program
    [Arguments]    @{args}    &{config}
    Run Process    program.py    @{args}    &{config}

The last example above shows how to create a wrapper keyword that accepts any positional or named argument and passes them forward. See `free named argument examples`_ for a full example with same keyword.

Free named arguments support with user keywords works similarly as kwargs work in Python. In the signature and also when passing arguments forward, &{kwargs} is pretty much the same as Python's **kwargs.

Starting from Robot Framework 3.1, user keywords support `named-only arguments`_ that are inspired by Python 3 keyword-only arguments. This syntax is typically used by having normal arguments after variable number of arguments (@{varargs}). If the keywords does not use varargs, it is possible to use just @{} to denote that the subsequent arguments are named-only:

*** Keywords ***
With Varargs
    [Arguments]    @{varargs}    ${named}
    Log Many    @{varargs}    ${named}

Without Varargs
    [Arguments]    @{}    ${first}    ${second}
    Log Many    ${first}    ${second}

Named-only arguments can be used together with positional arguments as well as with free named arguments. When using free named arguments, they must be last:

*** Keywords ***
With Positional
    [Arguments]    ${positional}    @{}    ${named}
    Log Many    ${positional}    ${named}

With Free Named
    [Arguments]    @{varargs}    ${named only}    &{free named}
    Log Many    @{varargs}    ${named only}    &{free named}

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:

*** Test Cases ***
Example
    With Varargs    named=value
    With Varargs    positional    second positional    named=foobar
    Without Varargs    first=1    second=2
    Without Varargs    second=toka    first=eka
    With Positional    foo    named=bar
    With Positional    named=2    positional=1
    With Free Named    positional    named only=value    x=1    y=2
    With Free Named    foo=a    bar=b    named only=c    quux=d

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.

*** Keywords ***
With Default
    [Arguments]    @{}    ${named}=default
    Log Many    ${named}

With And Without Defaults
    [Arguments]    @{}    ${optional}=default    ${mandatory}    ${mandatory 2}    ${optional 2}=default 2    ${mandatory 3}
    Log Many    ${optional}    ${mandatory}    ${mandatory 2}    ${optional 2}    ${mandatory 3}

Robot Framework has also another approach to pass arguments to user keywords than specifying them in cells after the keyword name as explained in the previous section. This method is based on embedding the arguments directly into the keyword name, and its main benefit is making it easier to use real and clear sentences as keywords.

It has always been possible to use keywords like :name:`Select dog from list` and :name:`Selects cat from list`, but all such keywords must have been implemented separately. The idea of embedding arguments into the keyword name is that all you need is a keyword with name like :name:`Select ${animal} from list`.

*** Keywords ***
Select ${animal} from list
    Open Page    Pet Selection
    Select Item From List    animal_list    ${animal}

Keywords using embedded arguments cannot take any "normal" arguments (specified with :setting:`[Arguments]` setting) but otherwise they are created just like other user keywords. The arguments used in the name will naturally be available inside the keyword and they have different value depending on how the keyword is called. For example, ${animal} in the previous has value dog if the keyword is used like :name:`Select dog from list`. Obviously it is not mandatory to use all these arguments inside the keyword, and they can thus be used as wildcards.

These kind of keywords are also used the same way as other keywords except that spaces and underscores are not ignored in their names. They are, however, case-insensitive like other keywords. For example, the keyword in the example above could be used like :name:`select x from list`, but not like :name:`Select x fromlist`.

Embedded arguments do not support default values or variable number of arguments like normal arguments do. Using variables when calling these keywords is possible but that can reduce readability. Notice also that embedded arguments only work with user keywords.

One tricky part in using embedded arguments is making sure that the values used when calling the keyword match the correct arguments. This is a problem especially if there are multiple arguments and characters separating them may also appear in the given values. For example, keyword :name:`Select ${city} ${team}` does not work correctly if used with city containing too parts like :name:`Select Los Angeles Lakers`.

An easy solution to this problem is quoting the arguments (e.g. :name:`Select "${city}" "${team}"`) and using the keyword in quoted format (e.g. :name:`Select "Los Angeles" "Lakers"`). This approach is not enough to resolve all this kind of conflicts, though, but it is still highly recommended because it makes arguments stand out from rest of the keyword. A more powerful but also more complicated solution, using custom regular expressions when defining variables, is explained in the next section. Finally, if things get complicated, it might be a better idea to use normal positional arguments instead.

The problem of arguments matching too much occurs often when creating keywords that `ignore given/when/then/and/but prefixes`__ . For example, :name:`${name} goes home` matches :name:`Given Janne goes home` so that ${name} gets value Given Janne. Quotes around the argument, like in :name:`"${name}" goes home`, resolve this problem easily.

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 basically matches any string. This logic works fairly well normally, but as just discussed above, sometimes keywords match more than intended. Quoting or otherwise separating arguments from the other text can help but, for example, the test below fails because keyword :name:`I execute "ls" with "-lh"` matches both of the defined keywords.

*** Test Cases ***
Example
    I execute "ls"
    I execute "ls" with "-lh"

*** Keywords ***
I execute "${cmd}"
    Run Process    ${cmd}    shell=True

I execute "${cmd}" with "${opts}"
    Run Process    ${cmd} ${opts}    shell=True

A solution to this problem is using a custom regular expression that makes sure that the keyword matches only what it should in that particular context. To be able to use this feature, and to fully understand the examples in this section, you need to understand at least the basics of the regular expression syntax.

A custom embedded argument regular expression is defined after the base name of the argument so that the argument and the regexp are separated with a colon. For example, an argument that should match only numbers can be defined like ${arg:d+}. Using custom regular expressions is illustrated by the examples below.

*** Test Cases ***
Example
    I execute "ls"
    I execute "ls" with "-lh"
    I type 1 + 2
    I type 53 - 11
    Today is 2011-06-27

*** Keywords ***
I execute "${cmd:[^"]+}"
    Run Process    ${cmd}    shell=True

I execute "${cmd}" with "${opts}"
    Run Process    ${cmd} ${opts}    shell=True

I type ${a:\d+} ${operator:[+-]} ${b:\d+}
    Calculate    ${a}    ${operator}    ${b}

Today is ${date:\d{4\}-\d{2\}-\d{2\}}
    Log    ${date}

In the above example keyword :name:`I execute "ls" with "-lh"` matches only :name:`I execute "${cmd}" with "${opts}"`. That is guaranteed because the custom regular expression [^"]+ in :name:`I execute "${cmd:[^"]}"` means that a matching argument cannot contain any quotes. In this case there is no need to add custom regexps to the other :name:`I execute` variant.

Tip

If you quote arguments, using regular expression [^"]+ guarantees that the argument matches only until the first closing quote.

Supported regular expression syntax

Being implemented with Python, Robot Framework naturally uses Python's :name:`re` module that has pretty standard regular expressions syntax. This syntax is otherwise fully supported with embedded arguments, but regexp extensions in format (?...) cannot be used. Notice also that matching embedded arguments is done case-insensitively. If the regular expression syntax is invalid, creating the keyword fails with an error visible in `test execution errors`__.

Escaping special characters

There are some special characters that need to be escaped when used in the custom embedded arguments regexp. First of all, possible closing curly braces (}) in the pattern need to be escaped with a single backslash (}) because otherwise the argument would end already there. This is illustrated in the previous example with keyword :name:`Today is ${date:\\d{4\\}-\\d{2\\}-\\d{2\\}}`.

Backslash (:codesc:`\\`) is a special character in Python regular expression syntax and thus needs to be escaped if you want to have a literal backslash character. The safest escape sequence in this case is four backslashes (\\) but, depending on the next character, also two backslashes may be enough.

Notice also that keyword names and possible embedded arguments in them should not be escaped using the normal `test data escaping rules`__. This means that, for example, backslashes in expressions like ${name:w+} should not be escaped.

Using variables with custom embedded argument regular expressions

Whenever custom embedded argument regular expressions are used, Robot Framework automatically enhances the specified regexps so that they match variables in addition to the text matching the pattern. This means that it is always possible to use variables with keywords having embedded arguments. For example, the following test case would pass using the keywords from the earlier example.

*** Variables ***
${DATE}    2011-06-27

*** Test Cases ***
Example
    I type ${1} + ${2}
    Today is ${DATE}

A drawback of variables automatically matching custom regular expressions is that it is possible that the value the keyword gets does not actually match the specified regexp. For example, variable ${DATE} in the above example could contain any value and :name:`Today is ${DATE}` would still match the same keyword.

The biggest benefit of having arguments as part of the keyword name is that it makes it easier to use higher-level sentence-like keywords when writing test cases in `behavior-driven style`_. The example below illustrates this. Notice also that prefixes :name:`Given`, :name:`When` and :name:`Then` are `left out of the keyword definitions`__.

*** Test Cases ***
Add two numbers
    Given I have Calculator open
    When I add 2 and 40
    Then result should be 42

Add negative numbers
    Given I have Calculator open
    When I add 1 and -2
    Then result should be -1

*** Keywords ***
I have ${program} open
    Start Program    ${program}

I add ${number 1} and ${number 2}
    Input Number    ${number 1}
    Push Button     +
    Input Number    ${number 2}
    Push Button     =

Result should be ${expected}
    ${result} =    Get Result
    Should Be Equal    ${result}    ${expected}

Note

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

Similarly as library keywords, also user keywords can return values. Typically return values are defined with the :setting:`[Return]` setting, but it is also possible to use BuiltIn_ keywords :name:`Return From Keyword` and :name:`Return From Keyword If`. Regardless how values are returned, they can be `assigned to variables`__ in test cases and in other user keywords.

The most common case is that a user keyword returns one value and it is assigned to a scalar variable. When using the :setting:`[Return]` setting, this is done by having the return value in the next cell after the setting.

User keywords can also return several values, which can then be assigned into several scalar variables at once, to a list variable, or to scalar variables and a list variable. Several values can be returned simply by specifying those values in different cells after the :setting:`[Return]` setting.

*** Test Cases ***
One Return Value
    ${ret} =    Return One Value    argument
    Some Keyword    ${ret}

Multiple Values
    ${a}    ${b}    ${c} =    Return Three Values
    @{list} =    Return Three Values
    ${scalar}    @{rest} =    Return Three Values

*** Keywords ***
Return One Value
    [Arguments]    ${arg}
    Do Something    ${arg}
    ${value} =    Get Some Value
    [Return]    ${value}

Return Three Values
    [Return]    foo    bar    zap

BuiltIn_ keywords :name:`Return From Keyword` and :name:`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 :setting:`[Return]` setting discussed above.

The first example below is functionally identical to the previous :setting:`[Return]` setting example. The second, and more advanced, example demonstrates returning conditionally inside a `for loop`_.

*** Test Cases ***
One Return Value
    ${ret} =    Return One Value  argument
    Some Keyword    ${ret}

Advanced
    @{list} =    Create List    foo    baz
    ${index} =    Find Index    baz    @{list}
    Should Be Equal    ${index}    ${1}
    ${index} =    Find Index    non existing    @{list}
    Should Be Equal    ${index}    ${-1}

*** Keywords ***
Return One Value
    [Arguments]    ${arg}
    Do Something    ${arg}
    ${value} =    Get Some Value
    Return From Keyword    ${value}
    Fail    This is not executed

Find Index
    [Arguments]    ${element}    @{items}
    ${index} =    Set Variable    ${0}
    FOR    ${item}    IN    @{items}
        Return From Keyword If    '${item}' == '${element}'    ${index}
        ${index} =    Set Variable    ${index + 1}
    END
    Return From Keyword    ${-1}    # Could also use [Return]

User keywords may have a teardown defined using :setting:`[Teardown]` setting.

Keyword teardown works much in the same way as a `test case teardown`__. Most importantly, the teardown is always a single keyword, although it can be another user keyword, and it gets executed also when the user keyword fails. In addition, all steps of the teardown are executed even if one of them fails. However, a failure in keyword teardown will fail the test case and subsequent steps in the test are not run. The name of the keyword to be executed as a teardown can also be a variable.

*** Keywords ***
With Teardown
    Do Something
    [Teardown]    Log    keyword teardown

Using variables
    [Documentation]    Teardown given as variable
    Do Something
    [Teardown]    ${TEARDOWN}