Dictionary as variable in Robot Framework: code runs ok but the IDE yields error - robotframework

I'm trying to set up a dictionary as a variable (so I can use it as a Resource and access its values from another file) and there is something that is driving me crazy.
Here is the code I have (just for testing purposes):
*** Settings ***
Documentation Suite description
Library Collections
*** Variables ***
&{SOME DICT} key1=value1 key2=value2
*** Test Cases ***
Dict Test # why $ instead of &?
${RANDOM VAR}= Get From Dictionary ${SOME DICT} key1
Log ${RANDOM VAR} WARN
If I run that, I got the expected result ([ WARN ] value1) BUT the IDE (PyCharm) is complaining about that ${SOME DICT} variable is not defined, and the dictionary declaration is not highlighted the same as variable or a list.
If I change that to &{SOME DICT} the IDE won't complain anymore, but the test fails with the following output:
Dict Test | FAIL |
Keyword 'Collections.Get From Dictionary' got positional argument after named arguments.
That is puzzling me to no end: why I have to use a $ instead of a & if it's a dictionary to make it work? Is there something I am doing wrong and it is just running by luck?
Thanks for any advice or guidance you may have!

Have a look into "Get from Dictionary" libdoc,looks like example is showing the same as your working snippet:
Name: Get From Dictionary
Source: Library (Collections)
Arguments: [dictionary, key]
Returns a value from the given ``dictionary`` based on the given ``key``.
If the given ``key`` cannot be found from the ``dictionary``, this
keyword fails.
The given dictionary is never altered by this keyword.
Example:
| ${value} = | Get From Dictionary | ${D3} | b |
=>
| ${value} = 2
Keyword implementation details are as follows:
try:
return dictionary[key]
except KeyError:
raise RuntimeError("Dictionary does not contain key '%s'." % key)
So indeed, Robot sends representation of dict content and not dict name thus value for key can be returned.
This is the same as direct call in python:
a = {u'key1': u'value1', u'key2': u'value2'}
print(a['key1'])
In the end, libdoc for that KW is not straightforward but your PyCharm plugin for Robot does not work properly in this case.
In RED Robot Editor (Eclipse based), proper case does not rise any warnings in editor, wrong-case provides error marker about arguments (better but still not clear what is exactly wrong. Blame minimalistic libdoc info).
ps. I am lead of RED project to be clear.

Simple Example to Use Key Value Variable in robot framework
Set value to dictionary
Get value from dictionary
&{initValues} Create Dictionary key1=value1 key2=value2
Set To Dictionary ${initValues} key1=newvalue1
Set To Dictionary ${initValues} key2=newvalue2
Set To Dictionary ${initValues} key3=newvalue3
${value} Get From Dictionary ${intialValues} key1

Related

Robotframework, How to define a dynamic variable name

I want to Define variable having dynamic name.
For example
${${FILE}{VAR}} Create List // ${FILE} = TEST, ${VAR} = VAR
Then I want to get variable named '${TESTVAR}'.
Here is my Summarized code...
*** Settings ***
Library SeleniumLibrary
Library ExcelLibrary
Library Collections
*** Variables ***
${VAR}= VAR
*** Keywords ***
Open Document And Assign Variable
[Arguments] ${FILE}
Open Excel Document filename=${FILE} doc_id=doc_var
${${FILE}${VAR}} Create List # It doesn't work ..
The previous answer is not really accurate.
This can be achieved exactly with robot's "Variables inside variables" feature like so:
FOR ${idx} IN RANGE 3
${var_name} = Catenate SEPARATOR=_ var ${idx}
Set Suite Variable ${${var_name}} ${idx}
END
you get:
   var_1=1
   var_2=2
   var_3=3
Please note that variables resolution is happening only for keywords arguments (at least for robot version 3.1.2 that I am using), therefore either one of 'Set test variable', 'Set Suite Variable' or 'Set Global Variable' keywords must be used. The following won't work:
FOR ${idx} IN RANGE 3
${var_name} = Catenate SEPARATOR=_ var ${idx}
${${var_name}} = ${idx}
END
results in:
    No keyword with name '${${var_name}} =' found.
For your list case you just need to create a list variable and reassign it to dynamically named variable using the above mentioned keywords
This is currently not possible with Robot Framework. You can use "variables inside variables" to resolve the values of variables (see the documentation on this topic) but not to resolve/set the name of the variable itself.
Though I am afraid that would be confusing anyway. Maybe you can explain your motivations and people can come up with another solution for your problem.

RF 3.0.4 nested dictionary syntax error if first key is number, dot notation doesn't work

I am using rf 3.0.4. I upgraded because of the dot notation upgrade (before I was using rf 2.9).
My problem is when I want to access a nested dictionary item and the first key (it is an id from db) is a number, I got a syntax error.
I have a nested dictionary: &{Attributes}
So what I want to do:
${Attributes.1000.name}
The syntax error I get:
Resolving variable '${Attributes.1000.name}' failed: SyntaxError: invalid syntax (<string>, line 1)
And what is working:
${Attributes["1000"]["name"]}
I'd like to use the dot notation, because it is more readable.
Do any of you know why it doesn't work?
It seems to me to be a limitation of Robot Framework. When a dictionary key item starts with a number (even when a string) then it will fail. In the below two test cases this is shown.
To me this sounds like a defect and you may want to log this as an issue with the project's GitHub issue log.
*** Settings ***
Library Collections
*** Variables ***
${name} MyName
&{person} name=${name}
&{person_valid} A1000=${person} A2000=${person}
&{person_invalid} 1000A=${person} 2000A=${person}
*** Test Cases ***
TC - Valid
${pers} Set Variable ${person_valid.A1000}
Dictionaries Should Be Equal ${pers} ${person}
${pers_name_1} Set Variable ${person_valid["A1000"]["name"]}
Should Be Equal As Strings ${pers_name_1} ${name}
${pers_name_2} Set Variable ${person_valid.A1000.name}
Should Be Equal As Strings ${pers_name_2} ${name}
TC - Fails
Run Keyword And Expect Error
... Resolving variable '\${person_invalid.1000A}' failed: SyntaxError: invalid syntax (<string>, line 1)
... Set Variable ${person_invalid.1000A}
Run Keyword And Expect Error
... Resolving variable '\${person_valid.1000A.name}' failed: SyntaxError: invalid syntax (<string>, line 1)
... Set Variable ${person_valid.1000A.name}

RED Robot Editor - One TestCase Dependent To Other [duplicate]

I have a large number of test cases, in which several test cases are interdependent. Is it possible that while a later test case is getting executed you can find out the status of a previously executed test case?
In my case, the 99th test case depends on the status of some prior test cases and thus, if either the 24th or the 38th fails I would like the 99th test case NOT to get executed at all and thus save me a lot of time.
Kindly, explain with some example if possible. Thanks in advance!
Robot is very extensible, and a feature that was introduced in version 2.8.5 makes it easy to write a keyword that will fail if another test has failed. This feature is the ability for a library to act as a listener. With this, a library can keep track of the pass/fail status of each test. With that knowledge, you can create a keyword that fails immediately if some other test fails.
The basic idea is, cache the pass/fail status as each test finishes (via the special _end_test method). Then, use this value to determine whether to fail immediately or not.
Here's an example of how to use such a keyword:
*** Settings ***
Library /path/to/DependencyLibrary.py
*** Test Cases ***
Example of a failing test
fail this test has failed
Example of a dependent test
[Setup] | Require test case | Example of a failing test
log | hello, world
Here is the library definition:
from robot.libraries.BuiltIn import BuiltIn
class DependencyLibrary(object):
ROBOT_LISTENER_API_VERSION = 2
ROBOT_LIBRARY_SCOPE = "GLOBAL"
def __init__(self):
self.ROBOT_LIBRARY_LISTENER = self
self.test_status = {}
def require_test_case(self, name):
key = name.lower()
if (key not in self.test_status):
BuiltIn().fail("required test case can't be found: '%s'" % name)
if (self.test_status[key] != "PASS"):
BuiltIn().fail("required test case failed: '%s'" % name)
return True
def _end_test(self, name, attrs):
self.test_status[name.lower()] = attrs["status"]
To solve this problem I'm using something like this:
Run Keyword if '${PREV TEST STATUS}'=='PASSED' myKeyword
so maybe this will be usable also for you.

How to check if a substring exists inside a 'Result' object with Robot Framework?

I am running the following test inside Robot Framework:
*** Settings ***
Documentation This initializes testrun.robot
Library Process
Library OperatingSystem
Library XML
Library Collections
Output Is A Valid XML File Against JATS format
Start Process xmllint --dtdvalid http://jats.nlm.nih.gov/archiving/1.1d3/JATS-archivearticle1.dtd ./output/nlm/out.xml shell=True
${result}= Wait For Process timeout=45 secs
Log ${result.stdout}
Log ${result.stderr}
Run Keyword Unless '${result.stderr} == ${EMPTY}' Should Contain ${result.stderr} element xref: validity error : IDREFS attribute rid references an unknown
The variable ${result.stderr} is a string that contains the substring 'element xref: validity error : IDREFS attribute rid references an unknown'. As far as I know, I'm not dealing with any whitespace errors or quotation problems, although I could be wrong. (I've been fiddling around with that for a while now.)
Thanks for the help!
Edit: The log that Robot Framework generates tells me that the process has finished (that's how I know what result.stderr contains.)
Consider this statement:
Run Keyword Unless '${result.stderr} == ${EMPTY}' ...
The keyword will never run because you aren't comparing two variables, you are simply checking whether the string literal string '${result.stderr} == ${EMPTY}' is not empty (and it's not, because it has 28 characters).
It is the same as if you did this in python:
if len('${result.stderr} == ${EMPTY}') > 0:
...
You need to put the single quotes around each variable separately so that you are passing a valid expression to the if statement, rather than a string that looks like a valid expression:
Run Keyword Unless '${result.stderr}' == '${EMPTY}' ...

How can I tell robot framework not to log a keyword?

In a robot framework test case I set a variable and then do a process.
Because the setting of the variable is not a very interesting bit of information, I don't want to include that in my report.
| Verifying STUFF |
| | ${endpoint}= | set variable | STUFF
| | Verify
My report contains this:
KEYWORD: ${endpoint} = BuiltIn.Set Variable STUFF
But I would rather not have it there. How can I tell Robot Framework to just not log that line?
------edit------
It looks like this should do it:
pybot --removekeywords NAME:SetVariable testcase.txt
But the Set Variable keywords are still there.
(And yes, I upgraded my robot framework to 2.8.3 to take advantage of this function.)
The best you can do is to use
Set Log Level NONE
but it will still log all the keyword calls, just not anything inside those.
Or if you call a python function which calls another function, then the call to the second function is not logged.
Like this:
*** Settings ***
Library lib.py
*** Test Cases ***
demo
Set Log Level NONE
${a} foo
xyzzy
*** Keywords ***
xyzzy
qwerty
qwerty
No Operation
Log 123
and lib.py being like this:
def foo():
abc = bar()
return abc
def bar():
c = 1
print c
return c
The problem is that when you assign a variable like ${var} = Keyword, the name of the keyword in Robot Framework outputs is ${var} = Keyword, not Keyword as you would expect. If your keyword is from a library or a resource file, its name will also be included like ${var} = MyLibrary.Keyword. The latter is a feature but the former is a bug that is hopefully fixed in RF 2.9.
An easy workaround for the keyword name, for now, including the variable name is using wildcards. Something like this ought to work for you:
--RemoveKeywords 'name:* = BuiltIn.Set Variable'
You can use --removekeywords or --flattenkeywords option on pybot to remove the content of keyword So if you have e.g. keyword "foo" that contains lot's of logging keywords, you can set "--flattenkeywords name:foo" option to pybot, and In the log you'll only see the primary keyword, but no keywords inside it.
http://robotframework.googlecode.com/hg/doc/userguide/RobotFrameworkUserGuide.html?r=2.8.3#removing-and-flattening-keywords
If you use a python library, the following monkey-patching works for me:
from robot.libraries.BuiltIn import BuiltIn
from robot.output.logger import LOGGER
import types
def _nothing(*args, **kwargs):
pass
def disable_keyword_logging(self):
self._logging_methods = (LOGGER.start_keyword, LOGGER.end_keyword)
LOGGER.start_keyword = types.MethodType(_nothing,LOGGER)
LOGGER.end_keyword = types.MethodType(_nothing,LOGGER)
def enable_keyword_logging(self):
LOGGER.start_keyword, LOGGER.end_keyword = self._logging_methods
Then when this script is run:
Disable keyword logging
Log Hello world
Enable keyword logging
the "Log" keyword is not logged to the output but the output is.
If you really want nothing (also no debug/info/warn information logged by the called keywords), you will still have to set the log level to "NONE".
Robot Framework doesn't log "global" variables as part of a variable table. Global is in quotation marks because Set Global Variable actually is logged, but if you initialize your variable like so...
*** Variables ***
${endpoint} stuff
*** Keywords ***
...then it will not be in the Log. Additionally, if you don't want anyone to see the variable at all if they're just looking at the front end of your testing suite, you can bury it in a Resource file and the call the Resource file.
Robot Framework logs your Set Variable keywords and results because Set Variable implies that you're setting a variable dynamically and might be setting it based on the result of a keyword, in which case you'd probably want to know what the result of the keyword is. If you're just creating a static variable, then no extra work beyond the table is required. Is a dynamic variable a required part of your code?

Resources