• Jobs
  • About
  • Choosing Selenium locators effectively February 7, 2011

    Updated (Aug 31, 2011): If you are using the WebDriver API in Selenium 2, you should look at the Locating page elements using WebDriver post. If you are using Selenium 1.x or Selenium-backed WebDriver (Selenium 2’s backward compatibility mode) then this page would still be relevant to you.


    Why is choosing the right locator important

    A big part of implementing your browser based automation solution effectively, is choosing locators wisely. There are 8 explicit locators: id, name, identifier, dom, xpath, link, css and ui that Selenium’s commands support to locate elements on the page to interact with. Using the right locator ensures your tests are faster, more reliable or has lower maintenance over releases. This document explains how and when to use these locators.

    I frequently say that QA should frequently engage with Developers and UI designers early so that they build high quality products. One of the ways these teams can collaborate to create highly testable code is by ensuring that all the elements on the page that you would need to interact with in your tests can be easily located.

    Selecting the right locator

    Let us look at 7 locators:

    id

    ids are the preferred way to locate elements on a page for 2 main reasons:

    • According to w3c ids are supposed to be unique on an html page. This makes ids a very explicit and reliable way to locate elements on the page.
    • Also, all browsers also have highly efficient methods to get an object on the page using their ids. This makes id locators the fastest type of locator in Selenium.

    Let us now look at how to use id locators using this html code as an example:

    </pre>
    <form name="loginForm">Login Username: <input id="username" type="text" name="login" />
     Password: <input id="password" type="password" name="password" />
     <input type="submit" name="login" value="Login" /></form>
    <pre>
    

    In the above code, the username and password text fields are can be set using their ids. The locators for them would be

    id=username
    id=password

    The way you would use these locators in your test code would be:

    public void login(String username, String password) {
        browser.type("id=username", username);
        browser.type("id=password", password);
        ...
    }
    

    Even though this is a great locator, obviously it is not realistic for all objects on a page to have ids. There are several reasons why elements need to have ids so that the functionality of the page is achieved, however, ids should also be added to elements that are frequently interacted within tests.

    In some cases, the ids on an element cannot be reliably used. For instance, if you are displaying objects stored in the database, often the objects ids contain the database id in it, for instance book1347 could be the id for book who’s database id is 1347.

    name

    ids are generally added to elements when they want to be referenced from css and names are added to form fields. When referencing element from javascript either can be used. From a test automation standpoint the takeaway is that whenever id is not available/ usable, often name can be used.

    There is one big difference between id and name locators though … name attributes don’t have to be unique in a page. If there are multiple elements with the same name, then just using the name would select the first element in the page. If you want to choose a specific element, you can filter it using a different attribute. Let us look at this example:

    </pre>
    <form id="loginForm">Login Username: <input type="text" name="login" />
     Password: <input type="password" name="password" />
     <input type="submit" name="login" value="Signin" /></form>
    <pre>
    

    Here you can reference the Login Username field using:

    name=login
    or if you want to be more specific (recommended) use
    name=login type=text

    and you can reference the submit button using:
    name=login value=Signin
    or
    name=login Signin
    or
    name=login type=button

    As you can see, if you are using the value attribute, you can just put it’s value and that would work. For any other attribute you have put the attribute name and value as the filter.

    Using these techniques should work in most cases … but what happens if there is another form added in the future where another text field is added with the name as login? Not very likely, but, if it did happen, your test could break. So, names can sometimes be problematic.

    identifier

    This is an identifier that uses the id attribute first. If it cannot find an element with the id attribute value match, it looks for the name attribute value match. So,
    identifier=login
    would reference an element

    <input id="login" type="text" />
    if it exists, otherwise it would target

    <input name="login" type="text" />

    link

    This locator identifies elements by the text in them. This is usually used for hyperlinks.

    
     ...
     <a href="signin.html">Sign In</a> to modify your account details.
     ...
    
    

    To click this hyperlink using the anchor tag’s text, you can use the link locator:
    link=Sign In

    Again here there could be multiple elements with text “Sign In” but here the first one is selected.

    xpath

    XPath is a very powerful language to express which element to target. If you use it correctly, it can produce very reliable and low maintenance locators, but if you use it incorrectly, it can create very brittle test cases.

    Let us see some examples:

    </pre>
     ...
    <table>
    <tbody>
    <tr id="item1">
    <td class="name item">Mp3 Download: foobar.mp3</td>
    <td class="name item"><input class="name item formfield disabled" type="text" name="qty" /></td>
    </tr>
    <tr id="item2">
    <td class="name item">Mp3 Player</td>
    <td class="name item"><input id="item2_quantity" class="name item formfield required" type="text" name="qty" /></td>
    </tr>
    </tbody>
    </table>
    <pre>
    

    You can target the highlighted input field using


    xpath=//table/tr[2]/td/input
    xpath=//input[@id='item2_quantity']
    xpath=(//table[@name='cart']//input)[2]
    xpath=//input[contains(@class='required')]
    xpath=//input[contains(@class='required')] and type='text']

    As you can guess, some of these expressions will not be as reliable as others. Of these //table/tr[2]/td/input is the worst because it would break even with slight modifications to the page structure. It can take some time to learn XPath if you aren’t familiar with it but it is worth the time to learn it. In any case do not rely on tools, including selenium IDE, to generate the right xpath expression for you – they are usually the worst.

    So, if xpath’s are so versatile, why doesn’t everyone prefer these? It’s because they are often the slowest, especially on IE! There are some ways you can make XPath’s faster, which I will cover in a later post.

    css

    css locators can be used to identify a large number of variations. For example:

    </pre>
     ...
    <table>
    <tbody>
    <tr id="item1">
    <td class="name item">Mp3 Download: foobar.mp3</td>
    <td class="name item"><input class="name item formfield disabled" type="text" name="qty" /></td>
    </tr>
    <tr id="item2">
    <td class="name item">Mp3 Player</td>
    <td class="name item"><input id="item2_quantity" class="name item formfield required" type="text" name="qty" /></td>
    </tr>
    </tbody>
    </table>
    <pre>
    

    Let us look at some of the css locator variations possible for the highlighted input field.


    css=input.required
    css=input[class~='required']
    css=input.required[type='text']
    css=#item2_quantity
    css=input#item2_quantity

    css locator may not be as expressive as xpath, but it generally executes faster.

    dom

    DOM stands for Document Object Model. DOM is convention for representing objects in HTML documents. Selenium interact with these objects using javascript.

    </pre>
    <form id="loginForm">Login Username: <input id="username" type="text" name="username" />
     Password: <input type="password" name="password" />
     <input type="submit" name="login" value="Log in" /></form>
    <pre>
    

    In this page, you can locate the highlighted input field using:

    document.forms[0].elements[0]
    document.forms['loginForm'].elements['username']
    dom=document.forms['loginForm'].username
    dom=document.getElementById('username')

    Implicit locator

    If you do not explicitly specify the locator strategy, Selenium checks if

    • the locator starts with document, if it does, it uses dom locator
    • if it starts with // it uses the xpath locator
    • otherwise, it uses the identifier locator

    Remember, some xpath expressions do not start with //, for instance, (//table[@name='cart']//input)[2] is a valid xpath expression but Selenium does not associate it with the xpath locator and tries to use identifier instead … and fails.

    Final Thoughts

    There are two types of Selenium implementations I have seen

    1. one in which the test cases contain direct selenium calls and so the locators are defined in the tests
    2. and second in which test cases use reusable functions in which the locators are declared at runtime.

    In the first case the test authors have a lot of control with regards to how the elements are located. This is great if the test authors know how to select the right locators, but it also usually means

    1. the tests are tightly tied with Selenium as a tool and so in the future if they had to switch tools, it would be a lot harder to do so.
    2. there is a possibility of lot more effort modifying the tests when the UI undergoes significant changes.

    Often in the 2nd case creating a reusable function requires using a smart expression generation logic that can use the right locator based on factors like parameters passed to the function or inspecting elements on the page. If you did want to generate only one type of locator in your functions though, you will often end up choosing css or xpaths. If done right, in this case you will probably have a low maintenance function because of lesser duplication, but it will often be at a cost of slower execution speeds.

    I usually end up choosing one of the two approaches based on how large the application under test is and how often and how much regression areas change in the product. If its a small application or if the regression areas doesn’t change to a large degree frequently, I end up with the first implementation path. If I end up choosing the second, I frequently set time aside to look at how well the reusable functions are generating the most efficient expressions.

    Further Reading:

    Posted by Rahul Poonekar in : Selenium

    9 responses to “Choosing Selenium locators effectively”

    1. Akiaa says:

      Hi,
      I am a new bee to Selenium. I am trying to figure our how to locate css locator of a given element. I am trying to click select button on a page and Selenium RC is not able to recognise it.So thought of using css locator, I did try to look it up using firebug, but couldnt figure out how to do it.Can you help?

      Thanks,

    2. Rahul Poonekar says:

      Sorry for the late reply. Do you still need help with this? If so, can you describe what are you trying to achieve?

    3. Devu Byju says:

      Hi,

      I’m very new to Selenium. I’m trying to figure how to locate an element with a given class? I couldn’t use the button id as it is dynamic. From the below code, what should be the possible locator i can use?

      Also, Like the above question, how can i find the CSS location in a code? I right clicked on the above code using firebug and selected copy CSS, I get the below value? How can i specify this as a locator?

      html.x-border-box body#ext-gen1013.x-body div#ext-gen1041.x-box-inner div#emc-itoi-embedded-center.x-container div#ext-gen1082.x-box-inner div#db-main.x-container div#emc-notifications.x-panel div#ext-gen1138.x-panel-body div#ema-tbpp-008.x-container div#ext-gen1231.x-box-inner div#container-1084.x-container div#ema-003007.x-panel div#ext-gen1235.x-panel-body div#ext-gen1237.x-column-inner div#ema-portalcolumn-1085.x-container div#ema-portlet-010.x-panel div#header-1093.x-panel-header div#ext-gen1251.x-panel-header-body div#ext-gen1253.x-box-inner div#tool-1098.x-tool img#ext-gen1293.x-tool-config

      Thanks,
      Devu

    4. Rahul Poonekar says:

      Can you share the element as displayed in the html pane instead? But, assuming you have a button element something like , I would first look into using a css or xpath expression referring to the value attribute to locate the button
      css=input[value=’Save’]
      xpath=//input[@value=’Save’]

    5. Reena says:

      I have HTML page as

      No locate the slider and make it click on certain position ( title valu varies with the position value )
      Do I give the xpath as self.browser.find_element_by_xpath(“//div[@id=’Sli2-bar’]/title=”93.38842975206612”.click()
      or
      find_element_by_xpath(“//div[@id=’Sli2-bar’] and @title=”93.38842975206612”.click()

    6. Reena says:

      Sorry ,I did not paste the HTML code
      –div id=”Sli2-grip” class=”sapUiSliGrip” aria-disabled=”false” aria-label=”Slider >>2″ aria-live=”assertive” aria-valuemax=”100″ aria-valuemin=”0″ aria->>orientation=”horizontal” role=”slider” title=”93.38842975206612″ tabindex=”0″ >>style=”left: 447px;” aria-valuenow=”93.38842975206612″ aria-valuetext=”Value >>93.38842975206612″>▲

    7. Jean says:

      Helo Devu Byju,
      You are trying to copy a config of a library of EXT.JS, its better you use the labrery full.

    8. krishna says:

      Hi I am new to Selenium and I have record a script while running i am getting Element id = button-1040-btnEl(which is randomly generating on the website not found. here is the source code.

      assertText
      id=button-1040-btnEl

      I will appreciated your help.
      click
      id=button-1040-btnEl

    9. Ganesh says:

      Hi,I have the HTML source code

      In this I want a XPATH locator to the element with value 19500, so that I can use selenium RC get_value($locator) to return 19500.

      If you notice there are two Tables both without names.
      Without unique identifiers, and the element 19500 also does not have name or id identification.

      How do I do this ?

      GE.HwMgr.192.168.1.40.Dr[0].HWitems

      Name
      mDValue

      Description

      Properties
      READ WRITE -60000.0 – 60000.0

      Width
      4

      Type
      HWITEM

      Default
      0.000000

      Current
      19500.000000

    Leave a Reply

    Your email address will not be published. Required fields are marked *