I have written a keyword which calls a sub-keyword. Now the sub keyword is returning two values which I am trying to store it in an array assigned to main keyword. When I log to console the returned values, it gives an error message saying - Expected list-like value, got string
I have tried the below workarounds(failed):
1. Attempt1:
#{Items1} Run keyword if '${INACTIVEStateCount}'!='0' INACTIVE OTOID selector ${StateCount}
Log To console Values are ${Items1}
2. Attempt2:
#{Items1} Run keyword if '${INACTIVEStateCount}'!='0' INACTIVE OTOID selector ${StateCount}
${Items2} Create List #{Items1}
Log To console Values are ${Items2}
Main keyword:
#{Items1} Run keyword if '${INACTIVEStateCount}'!='0' INACTIVE OTOID selector ${StateCount}
${Items2} Create List #{Items1}
Log To console Values are ${Items2}
SubKeyword:
INACTIVE OTOID selector
[Documentation] Used to select Inactive OTOIDs
[Arguments] ${StateCount} ${LocatorWaitTime}=${defaultWait time}
:FOR ${INDEX} IN RANGE 1 ${StateCount}
\ ${Check}= Run keyword and return status Page should contain element xpath=//*#id='lookupFiberDetailsResult']/table/tbody/tr[${INDEX}]/td[4][text()='INACTIVE']
\ ${OTO_IDStatus} Run keyword if '${Check}'=='True' Get Text xpath=//*[#id='lookupFiberDetailsResult']/table/tbody/tr[${INDEX}]/td[4][text()='INACTIVE']
\ Log to Console Loop${INDEX} - OTOID:${OTO_IDStatus}
\ ${OTO_ID} Run keyword if '${OTO_IDStatus}'!='None' Get Text xpath=//*[#id='lookupFiberDetailsResult']/table/tbody/tr[${INDEX}]/td[2]
\ ${LineIdentifier} Run keyword if '${OTO_IDStatus}'!='None' Get Text xpath=//*[#id='lookupFiberDetailsResult']/table/tbody/tr[${INDEX}]/td[1]
\ Run keyword if '${OTO_IDStatus}'!='None' Log to Console OTOID is: ${OTO_ID}
\ Run keyword if '${OTO_IDStatus}'!='None' Log to Console OTOID is: ${LineIdentifier}
\ Run Keyword If '${OTO_IDStatus}'!='None' [Return] ${OTO_ID}
\ Run Keyword If '${OTO_IDStatus}'!='None' [Return] ${LineIdentifier}
\ Exit For Loop if '${OTO_IDStatus}'!='None'
What I intend to achieve:
I am making the sub-keyword return two values: OTOID and LineIdentifier and store it in an array. And then use these two values separately for further use.
Actual:
When I log to console the values stored in array then I get an error saying:
Expected list-like value, got string
The issue is that you are trying to store the return values from the sub keyword as an array/list but returning strings from it.
*** Keywords ***
Main keyword
${OTO_ID} ${LineIdentifier} Run keyword if '${INACTIVEStateCount}'!='0' INACTIVE OTOID selector ${StateCount}
INACTIVE OTOID selector
Return From Keyword If '${OTO_IDStatus}'!='None' ${OTO_ID} ${LineIdentifier}
This will return the values ${OTO_ID} and ${LineIdentifier} as individual variables and not arrays.
Please note that I've ignored the other steps in the sub keyword just to make the answer simpler.
The line(s) that needs to be replaced:
Run Keyword If '${OTO_IDStatus}'!='None' [Return] ${OTO_ID}
Run Keyword If '${OTO_IDStatus}'!='None' [Return] ${LineIdentifier}
Update:
If you want to strictly return the values as list, use below code
*** Keywords ***
Main keyword
${Identifiers} Run keyword if '${INACTIVEStateCount}'!='0' INACTIVE OTOID selector ${StateCount}
INACTIVE OTOID selector
${Identifiers} Create List
.
<Your FOR loop here>
.
\ Run Keyword If '${OTO_IDStatus}'!='None' Append to List ${identifiers} ${OTO_ID} ${LineIdentifier}
Exit For Loop If '${OTO_IDStatus}'!='None'
Return From Keyword If '${OTO_IDStatus}'!='None' ${Identifiers}
Reference : Robot User guide
Related
I am a list which is - ['','','','Some-Value','',''] , I am trying to remove all the null/empty values from list so that the final result be ['Some-Value'].
Below is the code which I tried but so no luck so far
${list_size} = Get Length ${list}
For ${index} IN RANGE ${list_size}
${item} Get Form List ${list} ${index}
Run Keyword if '${item}' == '' Remove ${item} from list ${list}
enter code here
End
Can anyone help to solve this problem ? Thank you!
Robot framework enables you to use Evaluate keyword, and than it is just a python problem.
This code example produces the result you are looking for.
*** Variables ***
#{list_variable} ${empty} some text ${empty}
*** Test Cases ***
Replace a String
${cleared_list}= Evaluate [x for x in #{list_variable} if x]
Log ${cleared_list}
My intend is to skip the ${num2} and ${num4} arguments.
But I got an error saying:
Positional argument cannot be used after named arguments
I don't know why it considers the list as a positional argument, shouldn't it be a variable one?
*** Test Cases ***
Test
#{daysOff}= Create List Tuesday Friday
Keyword 30 num3=6 #{daysOff}
*** Keywords ***
Keyword
[Arguments] ${num1} ${num2}=5 ${num3}=10 ${num4}=15 #{list}
Log ${num1}
Log ${num2}
Log ${num3}
Log ${num4}
FOR ${item} IN #{list}
Log ${item}
END
To resolve the
Positional argument cannot be used after named arguments
error you should move the list argument before the named, positional arguments.
Note that you cannot move it before ${num1} as that would give another error:
Keyword 'Keyword' missing named-only argument 'num1'.
All in all:
*** Test Cases ***
Test
#{daysOff}= Create List Tuesday Friday
Keyword 30 #{daysOff} num3=6
*** Keywords ***
Keyword
[Arguments] ${num1} #{list} ${num2}=5 ${num3}=10 ${num4}=15
Log ${num1}
Log ${num2}
Log ${num3}
Log ${num4}
FOR ${item} IN #{list}
Log ${item}
END
I am not sure what's wrong in the below Get Request, when i run the script in Get Request link appended.
Issue :
GET Request : url=http://127.0.0.1:5000//http://127.0.0.1:5000/index.html
Please see the below code and the Report screenshot.
I am stuck here! Really appreciate for the help.
${url3} http://127.0.0.1:5000/
${BROWSER} chrome
*** Test Cases ***
BrokenLinksTest-ForPracticeSelenium-2ndPage
Open Browser ${url3} ${BROWSER}
Maximize Browser Window
VerifyAllLinksOn2ndPage
Close Browser
*** Keywords ***
VerifyAllLinksOn2ndPage
Comment Count Number Of Links on the Page
${AllLinksCount}= get element count xpath://a
Comment Log links count
Log ${AllLinksCount}
Comment Create a list to store link texts
#{LinkItems} Create List
Comment Loop through all links and store links value that has length more than 1 character
: FOR ${INDEX} IN RANGE 1 ${AllLinksCount}-1
\ Log ${INDEX}
\ ${link_text}= Get Text xpath=(//a)[${INDEX}] #<-- for what ? -->
\ ${href}= Get Element Attribute xpath=(//a)[${INDEX}] href
\ Log ${link_text}
\ log to console ("The link text is "${link_text}" & href is "${href}" ${INDEX})
\ ${linklength} Get Length ${link_text} #<-- you are checking text not href ? -->
\ Run Keyword If ${linklength}>1 Append To List ${LinkItems} ${href}
Log Many ${LinkItems}
Remove Values From List ${LinkItems} javascript:void(0) #<-- don't forget checking content on list -->
${linkitems_length} Get Length ${LinkItems}
Log Many ${LinkItems}
#{errors_msg} Create List
Create Session secondpage http://127.0.0.1:5000/
:FOR ${INDEX} IN RANGE ${linkitems_length}
\ Log Many ${LinkItems[${INDEX}]}
\ ${ret} get request secondpage ${LinkItems[${INDEX}]}
\ log to console ${ret}
\ log ${ret}
\ ${code} Run Keyword And Return Status Should Be Equal As Strings ${ret.status_code} 200
#\ log to console "Gonna link" ${LinkItems[${INDEX}]}
# \ click link ${LinkItems[${INDEX}]}
#\ Capture Page Screenshot
#\ Click Link link=${LinkItems[${INDEX}]}
\ Run Keyword Unless ${code} Append To List ${errors_msg} error :${LinkItems[${INDEX}]} | ${ret.status_code}
${check} Run Keyword And Return Status Lists Should Be Equal ${errors_msg} ${EMPTY}
Run Keyword Unless ${check} Fail Link \ assertion Failed with msg:\n#{errors_msg}
Sleep 1
Ok, the "problem" is with these two lines:
Create Session secondpage http://127.0.0.1:5000/
and:
${ret} get request secondpage ${LinkItems[${INDEX}]}
As your screens show, your list of items (#{LinkItems}) already contains full url links, e.g.: http://127.0.0.1:5000/index.html but the Create Session keyword adds another http://127.0.0.1:5000/ in front of each list item.
Think about it as BASE_URL set up by Create Session keyword and an endpoint, e.g. /index.html. Create Session and Get Request are used together, the former setting up BASE_URL, the latter the endpoint part of the URL. You can see the documentation for the Create Session keyword, it explains its second parameter:
url Base url of the server
To solve this, you'd need to store in #{LinkItems} only everything after last / (it seems so in your case), so for example only /index.html or /shop.html
following code is producing some problem:
API Setup
[Arguments] ${url} ${username} ${password} ${run}=True ${fail}=False
Run Keyword If ${run}
... Run Keywords
... ${passed}= Run Keyword And Return Status Setup ${url} ${username} ${password}
... AND Log To Console ${passed}
... AND Should Not Be Equal ${fail} ${passed}
When I try to execute that, my RF says: Variable '${passed}' not found.
The RED IDE also says
Multiple markers at this line:
• Variable 'passed' is used, but not defined
• Keyword name '${passed}=' contains variables. RED is unable to validate
arguments given to this keyword
Does the keywords "Run Keywords" not allow any assignments of values to variables and if so, is there any "best practice" way to do what I want to do?
Thanks!
The Run Keywords does not allow assignments, that's true. This comes from the parser - the purpose of the keyword is to run other ones; it comes to the line with the assignment (${passed}= Run...), tries to substitute the variable "passed" with its value so it can execute it, but at this point the var is still undefined - and it fails.
It looks like what you want to do is to run a particular keyword (Setup) only if a condition is true, and only if the condition is true then log its outcome, and assert it (the outcome) is the desired one.
This can be achieved by breaking up the block in two. The keyword Run Keyword If returns the value of the embedded keyword, so this will work:
${passed}= Run Keyword If ${run} Run Keyword And Return Status Setup ${url} ${username} ${password}
... ELSE Set Variable ${False}
If ${run} == True the Setup keyword will be ran, and {passed} will hold a True/False value did the execution pass. If ${run} != True we're setting ${passed} to False, just so it doesn't have a value of None (its value is not really important in this case, but doing that gives consistency in the datatypes it has).
And now the other two keywords can be in an if-block by themselves - do their work only when ${run} == True:
Run Keyword If ${run} Run Keywords Log To Console ${passed}
... AND Should Not Be Equal ${fail} ${passed}
It is true that Run Keywords does not allow variable assignment within
Make a keyword that contains everything inside Run Keywords and just call it instead.
For example
*** Test Cases ***
API Setup
[Arguments] ${url} ${username} ${password} ${run}=True ${fail}=False
Run Keyword If ${run} Keyword A
*** Keywords ***
Keyword A
# put everything you need here
I am looping values from an Excel file by using ${txnExcel} to search and compare the value. But sometimes ${txnExcel} is doesn't exist in the system. In that case I want to ignore the fail (as there is no need to compare value) and continue to the next ${txnExcel} by skipping the error.
open excel ${PATH_EXCEL}
${exp_row_count} get row count Sheet1
:For ${i} in range 1 ${exp_row_count}
\ ${excel_index} evaluate ${i}+1
\ ${txnExcel} Read Cell Data By Name Sheet1 B${excel_index}
\ ${ServiceTypeExcel} Read Cell Data By Name Sheet1 C${excel_index}
\ ${TransferAmountExcelOrigin} Read Cell Data By Name Sheet1 D${excel_index}
\ ${CurrencyExcel} Read Cell Data By Name Sheet1 E${excel_index}
\ ${TransferAmountExcel} set variable ${TransferAmountExcelOrigin} ${CurrencyExcel}
#=======================================================================================
\ input text name=id_or_tsn ${txnExcel}
\ click button name=Submit
\ wait until element is visible xpath=/html/body/div[2]/div[2]/div/div/div/div[1]/div[2] 5s
#===========================Compare Transaction Element================================================
\ ${txnCompare} get text xpath=/html/body/div[2]/div[2]/div/div/div/div[1]/div[2]
\ ${ServiceTypeCompare} get text xpath=/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/span
\ ${TransferAmountCompare} get text xpath=/html/body/div[2]/div[2]/div/div/div/div[5]/div[2]/span
\ ${CurrencyCompare} get text xpath=/html/body/div[2]/div[2]/div/div/div/div[4]/div[2]
\ should be equal as strings ${txnExcel} ${txnCompare} invalid TXN comparison[${i}]
\ should be equal as strings ${ServiceTypeExcel} ${ServiceTypeCompare} invalid ServiceType comparison[${i}]
\ should be equal as strings ${TransferAmountExcel} ${TransferAmountCompare} invalid Transfer Amount comparison[${i}]
\ should be equal as strings ${CurrencyExcel} ${CurrencyCompare} invalid Currency comparison[${i}]
#============================End Compare Transaction Element====================================================
The way I understood your question was that you cycle through items fetched from Excel. Sometimes the item in Excel does not exist in the application. If that is the case, then skip the checks for that item and fetch the next item from excel.
As correctly referenced by #pankaj mishra, the keyword Run Keyword and Ignore Error should be used. This keyword outputs 2 values (this is why you see 2 variables before it). The first one contains the status and the second one the actual value.
This is then used by the keyword Continue For Loop which breaks off this cycle when the keyword returned an error (FAIL). All the checks that followed (represented by the logging step) are then skipped.
*** Test Cases ***
TC
#{list} Create List ${3} ${6} ${15} ${21}
Log To Console \n
:FOR ${id} IN #{list}
\ ${status} ${result}
\ ... Run Keyword And Ignore Error Mock Element Exists ${id}
\
\ Continue For Loop If '${status}' == 'FAIL'
\ Log To Console No Failure for ${id}
*** Keywords ***
Mock Element Exists
[Arguments] ${id}
&{dic} Create Dictionary 3=1 6=2 18=6 21=7
[Return] ${dic['${id}']}
You can put a if condition on your code when dealing with ${txnExcel} .
Just check as below in your desired language.Below code is just a logic. Write it in your desired language.
if(${txnExcel} != NULL){
// put your compare code inside this if statement.
}
you can use Run keyword and ignore error or Run Keyword And Continue On Failure.
details about these keywords can be found from
http://robotframework.org/robotframework/latest/libraries/BuiltIn.html
also there is a good example about these keywords here
How to ignore Get Table Text from Cell, if xpath of cell not match
in your code , your can use below line
\ ${status} ${result}
\ ... Run Keyword And Ignore Error input text name=id_or_tsn ${txnExcel}
\ Continue For Loop If '${status}' == 'FAIL'
and one more time in last to skip error for ${txnexcel}
Run Keyword And Continue On Failure Should Be Equal As Strings
${txnExcel} ${txnCompare} invalid TXN comparison[${i}]
Even if the value or variable ${txnExcel} is not provided , it will continue to next KW.
Here the solution that work for me now .
open excel ${PATH_EXCEL}
${exp_row_count} get row count Sheet1
:For ${i} in range 1 ${exp_row_count}
\ ${excel_index} evaluate ${i}+1
\ ${txnExcel} Read Cell Data By Name Sheet1 B${excel_index}
\ ${ServiceTypeExcel} Read Cell Data By Name Sheet1 C${excel_index}
\ ${TransferAmountExcelOrigin} Read Cell Data By Name Sheet1 D${excel_index}
\ ${CurrencyExcel} Read Cell Data By Name Sheet1 E${excel_index}
\ ${TransferAmountExcel} set variable ${TransferAmountExcelOrigin} ${CurrencyExcel}
#=======================================================================================
\ input text name=id_or_tsn ${txnExcel}
\ click button name=Submit
\ ${found_txn} run keyword and return status wait until element is visible xpath=/html/body/div[2]/div[2]/div/div/div/div[1]/div[2] 5s
\ ${error_message} run keyword unless ${found_txn} get text xpath=/html/body/div[2]/div[2]
\ run keyword and continue on failure element should be visible xpath=/html/body/div[2]/div[2]/div/div/div/div[1]/div[2]
\ run keyword unless ${found_txn} continue for loop
#===========================Compare Transaction Element================================================
\ ${txnCompare} get text xpath=/html/body/div[2]/div[2]/div/div/div/div[1]/div[2]
\ ${ServiceTypeCompare} get text xpath=/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/span
\ ${TransferAmountCompare} get text xpath=/html/body/div[2]/div[2]/div/div/div/div[5]/div[2]/span
\ ${CurrencyCompare} get text xpath=/html/body/div[2]/div[2]/div/div/div/div[4]/div[2]
\ should be equal as strings ${txnExcel} ${txnCompare} invalid TXN comparison[${i}]
\ should be equal as strings ${ServiceTypeExcel} ${ServiceTypeCompare} invalid ServiceType comparison[${i}]
\ should be equal as strings ${TransferAmountExcel} ${TransferAmountCompare} invalid Transfer Amount comparison[${i}]
\ should be equal as strings ${CurrencyExcel} ${CurrencyCompare} invalid Currency comparison[${i}]