4

I need to click on element based on what value it contains..but I want to set this value in test run or keyword definition (best option is in the test I guess) How should I do it?

the variable containing xpath should look like that:

${DROPDOWN ITEMS}    xpath=//*[contains(@class,'listitem-element')]/span[contains(text(),'${second_number}')]

This locator works when I replace the variable with actual number like '002', but I want to have it more general..

In keyword definition I use it like:

Choose Value From Dropdown
     focus    ${DROPDOWN ITEMS}
     click element   ${DROPDOWN ITEMS}

and in test I just call the keyword

my question is where and how to set the variable value of ${second_number} variable used in xpath? PS:the xpath definition, keyword and test are each in separate files thank you!

neliCZka
  • 945
  • 1
  • 16
  • 27
  • Couldnt you just place it in the *** Variables *** section? and then call it later down the line? You can also define it within the keyword section. – Goralight Dec 05 '16 at 11:53
  • well, I want to have the locators separated from the test and logic because of maintanability - let's say in page object structure..I know it would be the easiest way to use the xpath directly in keyword with the variable, but i really don't want to have xpaths in keyword definitions...:-/ – neliCZka Dec 05 '16 at 11:55
  • OK - you mentioned that you would be ok with the varible being defined when you run the test correct? You can define them when you fire off the test suite using the arguments in the console or in the argument file. If that is what you want i can explain more in the answer. – Goralight Dec 05 '16 at 12:02
  • yes please, it would be great...i know how to pass variable values from test to keyword using arguments (or variable in name of keyword), but really have no idea how to do it in this case of mine...thank you!! – neliCZka Dec 05 '16 at 12:20

4 Answers4

4

I use similar approach in my SUT, as it works with fairly complex objects, both precreated and dynamically generated during the tests executions - and their main user-identifiable attribute is the displayed name. Here's simplified version of my flow, and it's based around string substitution.

Starting off from the variables file - a simple collection of selenium locators, the value of the locator has a "special" string, which will later be substituted:

*** VARIABLES ***
    ${DROPDOWN ITEMS}    xpath=//*[contains(@class,'listitem-element')]/span[contains(text(),'SELENIUM_PLACEHOLDER_CHANGE_ME')]

Then, in the keyword files there are private keywords for returning the proper locators, for example for this one:

*** KEYWORDS ***
    _Return Selenium Locator For The Dropdown Item Named
        [Documentation]    Verifies the desired dropdown item is valid, ando returns its locator (not Webelements!!)
        [Arguments]    ${name}

        # change the placeholder with the actual UI name
        ${loc}=    Replace String  ${DROPDOWN ITEMS}    SELENIUM_PLACEHOLDER_CHANGE_ME    ${name}

        # why? Rationale explained below
        Element Should Be Visible    ${loc}    message=The dropdown does not have an item called ${name}

        [Return]    ${loc}

Why the visibility check? Simple - to fail as early as possible if there's no such object currently in the SUT, and to have uniform error message, independent of how is the element further used (clicked on, checked for presence, attribute retrieval, etc.)

Then, a follow up user keyword for performing actions on the element uses the previous one:

    # the user keywords
    Choose Value From Dropdown
        [Documentation]    It does what it does :)
        [Arguments]    ${the value}

        ${loc}=    _Return Selenium Locator For The Dropdown Item Named    ${the value}

        # as you can see, no checks is the element real - that'she offloaded to the helper keyword ^
        Focus Element    ${loc}
        Click Element    ${loc}

Finally, the test cases use the keyword to work with any data you deem neaded:

*** TESTCASE ***
The dropdown should do X
    [Documentation]    Steps: 1, 2, 3, etc

    # do the normal steps you'do do
    Choose Value From Dropdown    my current value

This approach applies fairly well for negative tests also - for example, to check a value is not present, a test case would contain:

    Run Keyword And Expect Error    The dropdown does not have an item called no_such_element    Choose Value From Dropdown    no_such_element

Thus we're both using selenium checks for the absence of the element, and keeping the test case close to real-life expression - a description of what should happen, with no special syntax and SE keywords.

please excuse any typos and minor syntax omissions - it's not easy to type on a mobile that much, next time I'd think twice before taking it on :D

Todor Minakov
  • 19,097
  • 3
  • 55
  • 60
  • @neliCZka and it scales quite well, I use this strategy for virtually every object identifiable by its name in the system - more than 50 of them, without having faced any significant drawbacks. – Todor Minakov Mar 14 '17 at 06:12
  • It did not work for cases where I had to input number into the xpath, so I prepared my own python function and it looks it works pretty well :) def replace_substring_placeholder(original,replace_substr): return original.replace('PLACEHOLDER',str(replace_substr)) – neliCZka Mar 15 '17 at 11:59
  • @neliCZka I'm surprised it didn't work for you with a number - in RF all passed arguments by default are strings, but perhaps you're calling it with an argument like `${1}` - an int type. If that is the case - and you prefer to keep the logic in RF syntax, you could cast it in the `_Return Selenium...` keyword by `${name}= Convert To String ${name}` - the same you did in py. Anyways, glad you resolved it, and HTH. – Todor Minakov Mar 15 '17 at 17:52
2

You can define variables when you fire off your test suite by using arguments. Here is the documentation for it

Right now, you would leave your xpath as it is. Keeping the ${second_number} inside. Now you can either define it within the argument or within the argument file. They do the exact same thing, but one is neater. Just to get it working I would just worry about putting it directly in the console.

pybot -v second_number:002 nameOfTestFile.robot

This will tell pybot to create a variable called ${second_number} with the value of 002. It does not save this inside the test, so after the test is completed, it will forget the variable.

Once this works, you can then move this into a Argument file Or if you want you can even define it inside a Variable file where you can store all of your variables and then call them within the argument file / within the console.

Any questions do ask and ill try to help out

Goralight
  • 2,067
  • 6
  • 25
  • 40
0

how about using the set suite variable keyword?

becixb
  • 365
  • 1
  • 9
0

we can use Evaluate keyword for framing dynamically changing xpath

*** Variable ***

${common xpath}    xpath=//label[contains(text(), '{0}')]

${text to be replaced}    my name

*** Keyword ***

Frame xpath based on user input

${final xpath}    Evaluate    "${common xpath}".format("${text to be replaced}")

log ${final xpath}

RESULT

In ${final xpath}, you will have the "xpath=//label[contains(text(), 'my name')]"