Thursday, May 29, 2014

all roads lead to rome - options to locate element and elements

Selenium provides an interface to locate an element or elements on web page, SearchContext, both WebDriver and WebElement implement this inerface.

It has two methods,
  • findElement(By by)
  • findElements(By by)
  • indElement(By by) will return the first element meeting the search criteria or throw a NoSuchElementException. The findElements(By by) will return a list of elements meeting the criteria which can be empty. The parameter By has many subclasses giving users many options to use, they are,

  • By.ByClassName
  • By.ByCssSelector
  • By.ById
  • By.ByLinkText
  • By.ByName
  • By.ByPartialLinkText
  • By.ByTagName
  • By.ByXPath


  • These classes are not meant to be instantiated directly by users, users are required to use the correspondent static factory method to create them, for example, By.id(String id) will return a By.ById

    In my opinion, The By class in these methods is the best design strategy in Selenium WebDriver framework, it allows users to only one method to achieve many variations of the searching method. Without this By parameter, it would need The following methods,

  • findElementById(String id)
  • findElementsById(String id)
  • findElementByCssSelector(String css)
  • findElementsByCssSelector(String css)
  • findElementByClassName(String css)
  • findElementsByClassName(String css)
  • findElementByLinkText(String css)
  • findElementsByLinkText(String css)
  • findElementByName(String css)
  • findElementsByName(String css)
  • findElementByPartialLinkText(String css)
  • findElementsByPartialLinkText(String css)
  • findElementByTagName(String css)
  • findElementsByTagName(String css)
  • findElementByXPath(String css)
  • findElementsByXPath(String css)


  • Luckily, they didn't take this route.

    Joshua Block advised the same principle in his book, Effective Java, Avoid Strings when other types are more appropriate, please refer to Page 224 of Effective Java, Second Edition for more information.

    • Method Summary

      Methods 
      Modifier and Type Method and Description
      static By className(java.lang.String className)
      Finds elements based on the value of the "class" attribute.
      static By cssSelector(java.lang.String selector)
      Finds elements via the driver's underlying W3 Selector engine.
      static By id(java.lang.String id) 
      static By linkText(java.lang.String linkText) 
      static By name(java.lang.String name) 
      static By partialLinkText(java.lang.String linkText) 
      static static By tagName(java.lang.String name) 
      static By xpath(java.lang.String xpathExpression) 
      WebElement findElement(SearchContext context)
      Find a single element.
      abstract java.util.List<WebElement> findElements(SearchContext context)
      Find many elements.


    If there are too many choices, people will be overwhelmed with the options. A lot of people often wonder which By class they should use.

    There are some rule of thumb,
    ByName
  • If there is unique name for the input, use it, so it won't matter whether there is an id defined for that element, this is because inputs are meant to be sent back to server and the interface between browser is the name, so most input fields will have names. However,web servers don't require input fields have the unique names, so you may encounter some situation where multiple inputs sharing the same name. In that case, findElement will only return the first element. To locate the elements with the same name, findElements method can collect them into a List object, then other algorithms can be used to locate the element from the list.
  • ById
  • For non input elements, if there is an id associated with it, use the id, it makes the task easier,
  • For links, both linkText and partialLinkText can be used, but partialLinkText may return other links containing the partial link text
  • ByClassName
  • ClassName and TagName are not suitable for locating single element, they are used for locating a group of elements and since the search results are broader than other selectors, it may result in lower performance, however, I don't have benchmark data for this suspicion.
  • ByCssSelector and ByXpath
  • cssSelector and xpath are designed for advanced users, they require more knowledge on CSS and xpath of the XML DOM. Here are some links you can use for reference,




  • Let us practise on this field.

    <label for="shipping-zip" id="labelshipping-zip" class="text"><strong>Zip Code:&tl;/strong>
    <input type="text" class="text" name="shippingAddressDS.shipping_ROW0_zip" id="shipping-zip" size="10" maxlength="99" value=""  />
    </label>
    
    ByName
    // ☹ this is a bad example, please don't follow the style.
    webDriver.findElement(By.name("shippingAddressDS.shipping_ROW0_zip"));
    
    ById
    // ☹ this is a bad example, please don't follow the style.
    webDriver.findElement(By.id("shipping-zip"));
    
    ByCssSelector
    // ☹ this is a bad example, please don't follow the style.
    webDriver.findElement(By.cssSelector("input[id='shipping-zip'"));
    
    ByXpath
    // ☹ this is a bad example, please don't follow the style.
    webDriver.findElement(By.xpath("//input[@id='shipping-zip'"));
    
    You can see there are many different ways to locate the same element.

    These are the ways to locate the element, but they are not the ways that they should be used in the test automation projects, for example, when filling out a form and click a button, it usually need to locate multiple elements and call the sendKeys method on each of them, such as,

    // ☹ this is a bad example, please don't follow the style.
    try {
       WelElement webElement = webDriver.findElement(By.name("shippingAddressDS.shipping_ROW0_zip"));
       webElement.clear();
       webElement.sendKeys(zipCode);
    } catch(NoSuchElementException e) {
       handle(e);
    }
    
    This is just the code for one input field, if you are doing this way for all input fields on the form, your tests will be full of code.

    There is a better way than using Selenium directly, here is an example,



    So just call,
    //   ☺ This is a good example.
            put(BILLING_ZIP_______, address.zipcode);
    

    All Roads Lead to Rome, but there exists a shortest one.

    3 comments:

    1. This comment has been removed by a blog administrator.

      ReplyDelete
    2. This comment has been removed by a blog administrator.

      ReplyDelete
    3. This comment has nothing to do with my post, I will delete it soon. Thank you.

      ReplyDelete