Replacing ${variable} in json file containing 'ID' - robotframework

I have some issues with how to replace a variable appearing within test data. Let me explain. I am running tests data driven and lets say I have 2 test cases. One does something and returns an 'id'. This 'id' is then used by the 2nd test case to add another entry. Such dependency is currently unavoidable since the 'id' generated is unique each time and TestCase2 depends on it.
Currently when running testcase1 I get back an 'id' which I set as a suite variable. And then testcase2 uses this 'id' variable thus set. This works if the data is hardcoded into the test case. But when the data is abstracted into a test data file I dont know how to replace the '${id} in the test data.
As an example.
TestCase2 reads data from the json file like this.
"{\"query\":\"mutation updatedata($id: Int!, $details: String!) {\\r\\n updatedetaildata(input: { id: $id, details: $details })\\r\\n}\\r\\n\",\"variables\":{\"details\":\"{\\\"total_amount\\\": 523000}\",\"id\":${ID}}}"
What I would like to find out is 1. How to replace the ${id} with suite variable that I set after running TestCase1?
2. In another scenario, if I were to pass ${id} to TestCase2 as an argument how do I get it to replace the ${id} field in the test case data?
Here's my test case :
*** Settings ***
Suite Setup Run Keywords
... Generate Access Token AND
... Generate Random Number AND
... Generate Random Name AND
... Set Testrails Attribute 1 29
Test Teardown Add Test Result
Suite Teardown Send Report to Workchat
Resource ../../../../../Main/resources/importer.robot
*** Test Case ***
Create New Mission - Belanja (Whitelist)
[Documentation] This is new test case
[Tags] api_test
Set Test ID 9449
${payload} Get File api-test/Main/collections/engagement/testing/apitest/createnewtest.json
${payload} Convert to Json ${payload}
### Req body
${response}= GraphQl Request method=POST
... referrer_url=graphql/query
... payload_path=${payload}
... token=${token}
Set Global Variable ${response}
Log To Console ${response}
${payload}= set variable ${response}
${mission_id}= get value from json ${payload} $.data.misPinCreateMission.id
Log To Console ${mission_id}
${id}= set variable ${mission_id}
Set Suite Variable ${ID} ${id}
${template}= Get File api-test/Main/collections/engagement/testing/apitest/editmission.json
${template}= replace variables ${template}
${payload} Convert to Json ${payload}
### Req body
${response}= GraphQl Request method=POST
... referrer_url=graphql/query
... payload_path=${payload}
... token=${token}
Set Global Variable ${response}
Log To Console ${response}
## Assertion
${expected_json} Get File api-test/Main/assertions/expected-json/engagement/testing/apitest/editmission.json
${expected_json}= Convert To Json ${expected_json}
I got this error message :
Create New Mission: This test for Create N... ........{'errors': [{'message': "json body could not be decoded: invalid character 'd' looking for beginning of value"}],
Both answers both be great appreciated. Thank you

Here is one way:
Replace ${ID} in template.json to be a valid number:
"{\"query\":\"mutation updatedata($id: Int!, $details: String!) {\\r\\n updatedetaildata(input: { id: $id, details: $details })\\r\\n}\\r\\n\",\"variables\":{\"details\":\"{\\\"total_amount\\\": 523000}\",\"id\":0}}"
Then you can load and modify it:
*** Settings ***
Library Collections
Library OperatingSystem
*** Test Cases ***
Jsontest
${template}= Get File template.json
${template_d}= Evaluate json.loads(${template}) modules=json
Set To Dictionary ${template_d['variables']} id 123
Log To Console ${template_d}

Related

how to assert key in robot framework

I have trouble with assert keys in my code.
No keyword with name 'in {'name': '$.data[0].name'}' found.
my code is
*** Variables ***
&{name} name=$.data[0].name
*** Variables ***
Run keyword if 'name' in ${name} log name is in the log as expected
The way you've defined your test cases (assuming the second *** Variables *** is actually *** Test Cases ***), robot thinks Run keyword if is the name of a test case, and the first keyword is 'name' in ${name}
The solution is to give your test case a name.
There's also a problem that you have two spaces after 'name' which should give you a different error than what you're reporting. Also, the second table should be named *** Test Cases ***.
*** Test Cases ***
Example test case
Run keyword if 'name' in ${name} log name is in the log as expected
You should probably also use $name rather than ${name} so that robot injects the actual variable into the expression.
*** Test Cases ***
Example test case
Run keyword if 'name' in $name log name is in the log as expected

How to auto +1 increment at text

I wanted to append fixed text and +1 behind it.
I have tried to add variable into the text but seems it is invalid.
Test Case
Send Item
[Arguments] ${RECEIVER} ${CLASSIFICATION}
Add item
Choose Mail Recipient ${RECEIVER}
${TIME} Get Time
${TCNUMBER}= ${TC007-00}+1
Add desc [RegressionTestCase][${TCNUMBER}][${CLASSIFICATION}][${SENSITIVITY}] [${TIME}]
error:
No keyword with name '${TC007-00}+1' found.
The expected results are:
TC007-001, TC007-002, etc.
For this case you have to store your number that needs to be incremented somewhere. In my example it is a global variable called ${ID}. You should increment this one using the Evaluate keyword and before that you can simply concatenate it with the base number of the TC.
Based on where you want to do this ${ID} can be a global, suite or test variable, or even local. For more see the User Guide, Using Set Test/Suite/Global Variable keywords.
*** Variables ***
${TCNUMBER_BASE} TC007-00
${ID} 1
*** Test Cases ***
Append Test
${TCNUMBER}= Set Variable ${TCNUMBER_BASE}${ID}
${ID}= Evaluate ${ID} + 1
Log ${TCNUMBER}
${TCNUMBER}= Set Variable ${TCNUMBER_BASE}${ID}
${ID}= Evaluate ${ID} + 1
Log ${TCNUMBER}
${TCNUMBER}= Set Variable ${TCNUMBER_BASE}${ID}
${ID}= Evaluate ${ID} + 1
Log ${TCNUMBER}

Unable to see Set Test Message log value after test case marked as fail

I have the below code:
*** Settings ***
Library OperatingSystem
Library Process
Library String
*** Variables ***
#{MyList}= item items items2
${LogStr1} *HTML*
*** Test Cases ***
#Start Test#
[xxxxx] My tests
FOR ${item} IN #{MyList}
General test.out testProfile ${item}
[Template] Run Test
[Tags] TestTags
END
*** Keywords ***
Run Test
[Documentation] Run the test
[Arguments] ${type} ${profile} ${file} ${test}
When suite config is updated
And updated the config in directory ${test}
Then publish test status
suite config is updated
[Documentation] Get the variables list
Log to Console "Updating get suite config file"
updated the config in directory ${test}
[Documentation] Get the variables list
Run keyword if "${test}" == "items" Stop Test "This is stop called"
publish test status
[Documentation] Create and check if any issue found
${LogStr}= Catenate Test Passed : Log created: Hello
Log to Console ${LogStr}
${LogStr1}= Catenate ${LogStr1} ${LogStr}\n
Set Test Variable ${LogStr1}
Set Test Message ${LogStr1}
Stop Test
[Documentation] Stop Execution
[Arguments] ${FIALUREMSG}
Log To Console ${FIALUREMSG}
${LogStr1}= Catenate ${LogStr1} ${FIALUREMSG}
Fail ${LogStr1}
As per the code the test can be pragmatically made to fail at 1st second or third run. So when I have the code like:
Run keyword if "${test}" == "item" Stop Test "This is stop called"
in mentioned keyword, there are 2 test cases that passes for a suite but report states:
Now if I make the second test case to fail I get below test message logs:
Run keyword if "${test}" == "items" Stop Test "This is stop called"
in mentioned keyword, there are 2 test cases that passes for a suite but report states:
Similarly if
Run keyword if "${test}" == "items2" Stop Test "This is stop called"
And so on - hence it seems that "Set Test Message" log values are ignored in report message when a test case is marked as Fail. Note that below is the log.html content when I ran the code to mark the very first test case as failure:
Run keyword if "${test}" == "item" Stop Test "This is stop called"
In all my question is thus that if I want report.html file to show logs for all fail and passed test cases how can I achieve it?
If you check the documentation of the Set Test Message keyword it says that any failure will override the messages, but you have the option to override the failure message from the teardown.
In test teardown this keyword can alter the possible failure message,
but otherwise failures override messages set by this keyword. Notice
that in teardown the message is available as a built-in variable
${TEST MESSAGE}.
So what you can do is instead of calling Set Test Message, you could just save the messages into a test variable. Then you should add a teardown in which you call the Set Test Message and concatenate your test variable with the ${TEST MESSAGE}. For example:
*** Test Cases ***
Test
[template] Template
[setup] Set Test Variable ${MSG} ${EMPTY} # create empty test variable to store messages
1
3
2
5
6
4
6
[teardown] Set Test Message ${MSG}\n${TEST MESSAGE} # concatenate failure messages to normal test messages
*** Keywords ***
Template
[arguments] ${number}
No Operation
Run Keyword If ${number} == 2 Fail fail message ${number}
Run Keyword If ${number} == 4 Fail fail message ${number}
Set Test Variable ${MSG} ${MSG}\nMy test message ${number} # concatenate next test message
This example produces the following report:
With this approach you could have only the template tests in the particular robot suite as all test listed in the Test Cases table will invoke the template.
Other, completely different solution can be to get rid of the FOR loop as elements in #{MyList} are static. If you move the template into the Settings table and then manually list all iterations, you could separate each one of them into an independent test case. This way failure in one iteration won't affect the test message set in an other iteration. For example:
*** Settings ***
Test Template Template
*** Test Cases ***
Test1 1
Test2 2
Test3 3
Test4 4
Test5 5
Test6 6
*** Keywords ***
Template
[arguments] ${number}
No Operation
Run Keyword If ${number} == 2 Fail fail message ${number}
Run Keyword If ${number} == 4 Fail fail message ${number}
Set Test Message My test message ${number}
This would produce the following report:
You have a third option in addition to my other answer but it is a bit more advanced solution so I have decided to post it as a separate answer.
You can write a small test library in Python that would also act as a listener. In this listener library you would need three functions out of which two would be keyword and one would be a listener function.
First function is _log_message which is described in the listener API 2. This function will be called by the framework if any logging happens. So when a test fails this keyword will receive the log entry of the failure which can be saved for later use. This function, as it starts with _ is not available in a robot suite as keyword.
def _log_message(self, message):
if message['level'] == 'FAIL':
self.test_message = f"{self.test_message}\n{message['message']}" # concatenate failure message
Second function add_test_message will replace the Set Test Message keyword in your code. Its purpose will be similar, which is to append the messages you want to set as test messages. This can be called from a robot suite as a keyword.
def add_test_message(self, message):
self.test_message = f"{self.test_message}\n{message}" # concatenate normal test message
The last function set_final_test_message will set the actual test message for you. This keyword has to be called at the end of the test teardown to ensure no other failure will override your test message. It simply calls the Set Test Message keyword internally and sets the string created by the previous two functions. This can be called from a robot suite as a keyword.
def set_final_test_message(self):
"""
Call this keyword at the end of the test case teardown.
This keyword can only be used in a test teardown.
"""
BuiltIn()._get_test_in_teardown('Set Final Test Message') # Check if we are in the test teardown, fail if not.
BuiltIn().set_test_message(self.test_message) # Call Set Test Message internally
As the purpose of the library is to set test messages the library scope should be TEST CASE. This means that a new library object will be created before every test case, effectively resetting any messages set by previous tests.
Here is the whole code of the library (TestMessageLibrary.py):
from robot.libraries.BuiltIn import BuiltIn
class TestMessageLibrary(object):
ROBOT_LIBRARY_SCOPE = 'TEST CASE' # define library scope
ROBOT_LISTENER_API_VERSION = 2 # select listener API
ROBOT_LIBRARY_VERSION = 0.1
def __init__(self):
self.ROBOT_LIBRARY_LISTENER = self # tell the framework that it will be a listener library
self.test_message = '' # internal variable to build the final test message
def _log_message(self, message):
if message['level'] == 'FAIL':
self.test_message = f"{self.test_message}\n{message['message']}" # concatenate failure message
def add_test_message(self, message):
self.test_message = f"{self.test_message}\n{message}" # concatenate normal test message
def set_final_test_message(self):
"""
Call this keyword at the end of the test case teardown.
This keyword can only be used in a test teardown.
"""
BuiltIn()._get_test_in_teardown('Set Final Test Message') # Check if we are in the test teardown, fail if not.
BuiltIn().set_test_message(self.test_message)
globals()[__name__] = TestMessageLibrary
This is an example suite with the library:
*** Settings ***
Library TestMessageLibrary
*** Test Cases ***
Test
[template] Template
1
3
2
5
6
4
6
[teardown] Set Final Test Message
Other Test
Add Test Message This should not conflict
[teardown] Set Final Test Message
*** Keywords ***
Template
[arguments] ${number}
No Operation
Run Keyword If ${number} == 2 Fail fail message ${number}
Run Keyword If ${number} == 4 Fail fail message ${number}
Add Test Message My test message ${number}
Example run called with robot --pythonpath ./ SO.robot. As the library is in the same directory as the suite file --pythonpath ./ was need to be able to import the library.
Report file:

Getting object assignment error when attempting to create a dictionary (Robot Framework)

I am attempting to create a dictionary based upon data in an xls spreadsheet. Below is my code. However, when performing the "set to dictionary" (last line of the code snippet below), I'm getting the following error: TypeError: 'unicode' object does not support item assignment. Any ideas as to what I'm missing?
*** Settings ***
Library ExcelLibrary
Library Collections
*** Variables ***
${PageSheetName} = Welcome Page
${WelcomeDict} = Create Dictionary
*** Test Cases ***
Excel Sandbox Test
Get Values from Spreadsheet
#Print out the Dictionary
*** Keywords ***
Get Values from Spreadsheet
# Open the file
Open Excel Current Directory ${Excel_File_Path}DataExtract.xls
# Get the number of rows
${iTotalRows} = Get Row Count ${PageSheetName}
# Loop through each row to get the data. Only need data from Columns A & B
: FOR ${iRowNum} IN RANGE 1 ${iTotalRows}+1
\ ${KeyVal} = Read Cell Data By Name ${PageSheetName} A${iRowNum}
\ ${Value} = Read Cell Data By Name ${PageSheetName} B${iRowNum}
\ Create the Welcome Page Dictionary ${KeyVal} ${Value}
Create the Welcome Page Dictionary
[Arguments] ${key} ${val}
Set To Dictionary ${WelcomeDict} ${key} ${val}
Consider this block of code:
*** Variables ***
${WelcomeDict} = Create Dictionary
It is creating a string variable with the value "Create Dictionary". You cannot call keywords in the *** Variables *** section.
From the robot framework user guide:
Variable tables are convenient, because they allow creating variables in the same place as the rest of the test data, and the needed syntax is very simple. Their main disadvantages are that values are always strings and they cannot be created dynamically.
If you want to initialize a dictionary, use &, and just don't provide any values:
*** Variables ***
&{WelcomeDict}=

Why is the value of a suite variable lost after importing it from a resource file?

From what I read about variable scopes and importing resource files in robotframework doc i would expect this to work (python 2.7, RF 2.8.7):
Test file:
*** Settings ***
Resource VarRes.txt
Suite Setup Preconditions
*** Variables ***
*** Test Cases ***
VarDemo
Log To Console imported [${TODAY}]
*** Keywords ***
Resource file:
*** Settings ***
Library DateTime
*** Variables ***
${TODAY} ${EMPTY} # Initialised during setup, see keyword Preconditions
*** Keywords ***
Format Local Date
[Arguments] ${inc} ${format}
${date} = Get Current Date time_zone=local increment=${inc} day result_format=${format}
[Return] ${date} # formatted date
Preconditions
${TODAY} = Format Local Date 0 %Y-%m-%d
Log To Console inited [${TODAY}]
However the output is:
inited [2015-03-20]
imported []
RF documentation states:
Variables with the test suite scope are available anywhere in the test
suite where they are defined or imported. They can be created in
Variable tables, imported from resource and ....
which I think is done here.
If I add a line to keyword Preconditions like this, it works:
Preconditions
${TODAY} = Format Local Date 0 %Y-%m-%d
Set Suite Variable ${TODAY}
Log To Console inited [${TODAY}]
The reason is that in the first line a local variable is defined, instead of initialising the test suite variable declared in the variable table. A paragraph in RF doc hints to that:
Variables set during the test execution either using return values
from keywords or using Set Test/Suite/Global Variable keywords always
override possible existing variables in the scope where they are set
I think a major drawback of RF is that you cannot define variables dynamically in the variable table. Setting the scope of a variable from within a keyword is something I try to avoid.
For dynamic variables, you might to use a variable file in Python. See the section "Implementing variable file as Python or Java class" in the User Guide.
For example, I use a variables.py with:
if platform.system() in ['Darwin', 'Linux']:
OS_FAMILY = 'unix'
elif platform.system() == 'Windows':
OS_FAMILY = 'windows'
else:
OS_FAMILY = 'unknown'
OS_FAMILY_IS_UNIX = OS_FAMILY == 'unix'
OS_FAMILY_IS_WINDOWS = OS_FAMILY == 'windows'
Then in the Robot Tests I can use the dynamic variables ${OS_FAMILY}, ${OS_FAMILY_IS_UNIX}, and ${OS_FAMILY_IS_WINDOWS} anywhere.
You should be able to do create your ${TODAY} variable.

Resources