The Robot Framework User Guide, section 6.6 Boolean arguments, says:
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 either empty or
case-insensitively equal to false or no. Other strings are considered
true regardless their value, and other argument types are tested using
same rules as in Python.
How do I replicate this behavior in my own user keywords?
The build-in keyword Convert To Boolean is stricter:
Converts the given item to Boolean true or false.
Handles strings True and False (case-insensitive) as expected,
otherwise returns item's truth value using Python's bool() method.
There are two functions in robot.utils for dealing with boolean arguments - is_truthy and is_falsy. DateTime uses is_falsy. To behave like that library, you could simple call the same function used by those libraries. Below is an implementation of is_falsy in Robot syntax and en example keyword using it to convert arguments. You could also convert the arguments as needed using the same evaluate statement and avoid inter-dependencies.
*** Test Cases ***
Boolean
[Template] Some Keyword
truE
${42}
FAlsE
no
${0}
*** Keywords ***
Some Keyword
[Arguments] ${option}
${option as bool} Is Truthy ${option}
Log To Console ${option} -> ${option as bool}
Is Truthy
[Arguments] ${arg}
${arg as bool} Evaluate robot.utils.is_truthy($arg) modules=robot
[Return] ${arg as bool}
Related
I have several Robot Framework keywords that return a basic string.
#keyword
def keyword_one():
return 'one'
#keyword
def keyword_two():
return 'two'
In a robot test case, I try to build a list with this items, but I can't figure out how to do that is one line.
*** Test Cases ***
Test Case List
#{my_list}= Create List Keyword One Keywork Two
I tried several syntax but can't make it work.
Of course, something like below works (hardcoded values).
*** Test Cases ***
Test Case List
#{my_list}= Create List one two
Thanks for your help.
At the time that I write this, robot doesn't have the ability to call keywords inline, so what you want isn't directly possible.
You could write your own keyword to do this, however. The keyword can accept multiple keywords as arguments, and use run keyword from the built-in library to run the keyword.
For example, the following keyword definition creates a keyword that creates a list of results from multiple keywords:
Keyword written in python
If you want to try this out, name the file example.py
from robot.libraries.BuiltIn import BuiltIn
builtin = BuiltIn()
def create_list_from_keywords(*keywords):
result = []
for keyword in keywords:
result.append(builtin.run_keyword(keyword))
return result
Example test
*** Settings ***
Library example.py
*** Keywords ***
Keyword one
return from keyword one
Keyword two
return from keyword two
*** Test cases ***
Example
#{actual}= create list from keywords Keyword one Keyword two
#{expected}= create list one two
Should be equal ${actual} ${expected}
Robot-based keyword definition
If you're uncomfortable with python, here's a robot-based keyword definition:
Create list from keywords
[Arguments] #{keywords}
[Return] #{result}
#{result}= create list
FOR ${keyword} IN #{keywords}
${keyword result}= Run keyword ${keyword}
Append to list ${result} ${keyword result}
END
At the moment you are adding the keywords to the list, not the values returned from running those keywords
You would need to call the keywords to get the returned values and add them to the list e.g.
*** Test Cases ***
Test Case List
${keyword_one_val} Keyword One
${keyword_two_val} Keyword Two
#{my_list}= Create List ${keyword_one_val} ${keyword_two_val}
log to console ${my_list}
which outputs:
['one', 'two']
Consider the following code:
In Utils.py:
#keyword
def get_compound_dictionary():
"""
https://docs.python.org/3/library/copy.html
An example compound dictionary
"""
return {'key1': 'value1', 'deep_dict': {'key2': 'value2'}}
In collection-library-tests.robot
*** Settings ***
Documentation A test suite utilizing all collection library keywords
Library Collections
Library Utils.py
# To run:
# robot --pythonpath Resources --noncritical failure-expected -d Results/ Tests/collection-
library-tests.robot
*** Test Cases ***
Use "Copy Dictionary" : Shallow Copy
${compound_python_dictionary} = get compound dictionary
&{shallow_copy} = Copy Dictionary ${compound_python_dictionary} deepcopy=False
# if we modify the contained objects (i.e. deep_dict) through the shallow_copy,
# the original compound_python_dictionary will see the changes in the contained objects
Set To Dictionary ${shallow_copy}[deep_dict] key2=modified
Log ${shallow_copy}
Log ${compound_python_dictionary}
Should Be Equal ${compound_python_dictionary}[deep_dict][key2] modified # fails, why?
The goal is stated in the test case as:
if we modify the contained objects (i.e. deep_dict) through the shallow_copy,
the original compound_python_dictionary will see the changes in the contained objects
Expected Result
Should Be Equal ${compound_python_dictionary}[deep_dict][key2] modified # passes
Observed Result
Note that I am using Robot FW version: Robot Framework 3.1.2 (Python
3.7.4 on linux)
Acc.to the documentation about Copy Dictionary:
The deepcopy argument controls should the returned dictionary be a
shallow or deep copy. By default returns a shallow copy, but that can be
changed by giving deepcopy a true value (see Boolean arguments). This > is a new option in Robot Framework 3.1.2. Earlier versions always
returned shallow copies.
Acc.to the documentation about Boolean Arguments:
Some keywords 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 regardless their value.
Note also that i tried also deepcopy=${False}, which yielded the same observed result.
The problem is not with the RF keyword (it very seldom is, they have extensive UT), but with the way you call it, namely this argument:
deepcopy=False
You may be thinking you are passing a boolean value, but in fact you are passing the string "False".
Inside the keyword's implementation there is this branching:
if deepcopy:
return copy.deepcopy(dictionary)
, and as a non-empty string evaluates to True, you are in fact getting a deep copy.
This is the way to pass a real False:
deepcopy=${False}
${rowcount}= Keyword1 Book1.xlsx 0
${length}= Set Variable ${rowcount}
${i} Set Variable 1
:FOR ${rowvalue} IN RANGE ${rowcount}
\ #{columnlist}= Keyword2 ${rowvalue}
Keyword2 is returning List of data. I want to check whether it is returning Empty List. Please help me with this?
The BuiltIn library has keywords Should be empty and Should not be empty which can be used to validate the length of a list.
Should Be Empty ${columnlist}
Should Not Be Empty ${columnlist}
Just in case someone else comes here looking for an answer which also addresses:
If ${columnlist} is not empty then I have to execute keyword below it. Is it possible using If statement? – Orsu Suni Jun 12 '17 at 4:44
Either one of these options should help (NOTE: only used within RIDE, I imagine they will work for others as well):
${len} Get Length ${columnlist} ---- will return zero if list is empty, you can then use ${len} in your conditional.
'#{columnlist}' == '#{EMPTY}' ---- should return true if list is empty, although so far I have only used it with RUN KEYWORD IF.
Alternatively you could run the following keyword:
${isEmpty} Run Keyword And Return Status Should Be Empty ${columnlist}
Then you get a boolean value in ${isEmpty} with whether the list is empty or not.
Statements/conditions in Robot are a bit confusing in my opinion. Previous suggestion 2. doesn't work for me. I'm using: Robot Framework 3.1.2 (Python 3.4.1 on win32)
I obtain expected solution:
*** Test Cases ***
TC1
${marker_files} Create List dummy3 dummy4 dummy5
Run keyword unless ${marker_files} == #{EMPTY} Operation on list
marker_files=${marker_files}
tc2
${marker_files} Create List #{EMPTY}
Run keyword unless ${marker_files} == #{EMPTY} Operation on list
marker_files=${marker_files}
*** Keywords ***
Operation on list
[Arguments] ${marker_files}=def
log to console \ndo something on list
consider the following robotframework code example:
*** Variables ***
${VAR_1} any value
${VAR_2} another value
*** Test Cases ***
For Example only
${VAR_1}= Some Conversion ${VAR_1}
${VAR_2}= Some Conversion ${VAR_2}
A User Keyword ${VAR_1} ${VAR_2}
Desired Notation
A User Keyword Some Conversion ${VAR_1} Some Conversion ${VAR_2}
*** Keywords ***
Some Conversion
[Arguments] ${value_to_convert}
${value_to_convert}= Catenate ${value_to_convert} Foobar
[Return] ${value_to_convert}
A User Keyword
[Arguments] ${arg1} ${arg2}
Log ${arg1}
Log ${arg2}
Question: is there a possibility to simplify the working testcase For Example only to the (non working) Desired Notation - or - can I somehow use the return value of a keyword to be passed as parameter without doing an explicit assignment before?
For clarification:
Some Conversion would be far more complex and is implemented within
a jrobotremotelibrary
Moving the assingments to A User Keyword is
no useful solution, because there will be many keywords with
different amount of parameters using the same functionality
Yes, it is possible. You can write your own keywords that call other keywords which are passed in as arguments
It would look something like this:
*** Keywords ***
A User Keyword
[Arguments] ${keyword1} ${var1} ${keyword2} ${var2}
${var1}= Run keyword ${keyword1} ${var1}
${var2}= Run keyword ${keyword2} ${var2}
log ${var1}
log ${var2}
You would use this keyword exactly like in your example:
A User Keyword Some Conversion ${VAR_1} Some Conversion ${VAR_2}
The argument value assignment of a keyword can not be the return value of another keyword.
As highlighted by #Bryan Oakly it is possible to mimic the appearance with some clever usage of Run keyword, as you highlighted this will not work as the assignment may not always be using keywords or even keywords with the same number of arguments.
So, the best approach is what you've already been doing, assigning the value to a variable and then the variable to the keyword argument.
I'm looking to recreate this in a test suite:
if value is None:
value = {}
I plan to be adding to the dictionary so the following does not work:
${default}= Create_Dictionary
${value2}= Set_Variable_If ${value} is ${None} ${default}
... ${value}
When I add a key to ${value2} it gets added to ${default} (most likely due to how Python passes around references).
If you are using robot framework 2.9 or greater you can use variables directly in python expressions by omitting the curly braces. If you have a robot variable named ${value}, you can reference it as $value where robot is expecting a python expression (such as with the evaluate keyword).
For example:
${value2}= evaluate {} if $value is None else $value
You can use Run Keyword If to run a keyword to set a variable. This can call Create Dictionary and create a new & unreferenced dictionary to set your variable.
${value}= Run Keyword If ${value} is ${None} Create Dictionary
... ELSE Set Variable ${value}