July 8, 2015

The-Internet: Storing locators for web elements

Writing automated test code to test against Dave Haeffner's mock site, The-Internet: Login Page.

This post is fourth in a series of six. Need to go back to the beginning?

Part Four: Storing locators in Enums

How to locate a web element? 


There are two main they locate web elements at work:

  • By ID
  • By CSS Selector
Earlier, I wrote about CSS Selectors when finding web elements.

With the Firefox browser and the Firebug plugin, to find the selectors of a web element, an automated tester can go to the Login Page of Dave Haeffner's test site, The-Internet, and right-click on each element listed:

Welcome to The-Internet


There are three main web elements on the page:  the Username textbox, the Password textbox, and the Login button.

If you have Firefox with the Firebug plugin and Firepath installed, you can go to each element, right-click on the element, and select "Inspect in Firepath":

Username textbox:

  • <input id="username" type="text" name="username"/>


Password textbox:

  • <input id="password" type="password" name="password"/>


Login button:

  • <button class="radius" type="submit">


We see that an automated tester can choose to select the web elements by either Id or by CSS Selector. For this example, just to illustrate how they are used, I will use CSS selectors. They are:
  • The Username textbox: [name='username']
  • The Password textbox: [name='password']
  • The Login button: [class='radius'][type='submit']
But the question is, once you find the selectors -- whether by CSS selectors or by id -- where would they be stored? It would be bad form to store them in code along with the automated test:

WebElement txtUsername = driver.findElement(By.id("username"));
System.out.println("Entering username...");
txtUsername.sendKeys(username);

... What if the id or name changes? How can you easily find the name of the elements if they need to be updated? 

One solution is to store these values as constants as Enums. which I covered briefly in my last blog entry.

We could have stored them in constants in Java by setting up some String variables to be public static final like so

public class LoginPageConstants {

public static final String USERNAME = "[name='username']";
public static final String PASSWORD = "[name='password']");
public static final String LOGIN_BUTTON = "[type='submit'][class='radius']";

}

These variable strings USERNAME, PASSWORD, and LOGIN are global constants. They can only have the preceding values that we have set, cannot be accidentally overwritten, wherever they are used. 

What are some of the problems of using public static final


"This pattern has many problems, such as:
  • "Not typesafe - Since a season is just an int you can pass in any other int value where a season is required, or add two seasons together (which makes no sense).
  • "No namespace - You must prefix constants of an int enum with a string (in this case SEASON_) to avoid collisions with other int enum types.
  • "Brittleness - Because [...] enums are compile-time constants, they are compiled into clients that use them. If a new constant is added between two existing constants or the order is changed, clients must be recompiled. If they are not, they will still run, but their behavior will be undefined.
  • "Printed values are uninformative - Because they are just ints, if you print one out all you get is a number, which tells you nothing about what it represents, or even what type it is.

How do we store the values of the locators?


Storing the values of the locators, we can use enums that may look like:

public enum LoginPageEnum implements ISelector{

HEADER("h2"),
  USERNAME("[name='username']"),
PASSWORD("[name='password']"),
LOGIN_BUTTON("[type='submit'][class='radius']");

}


Note: If you want to see the full example, you can examine my test code at test/java/pages/LoginPage.java.

With the above code, we have also captured the locator for the Header ("Login Page").

How do we access the values that we have stored? 


  • A String variable, id, is declared in the enum class, to stand for the locator for the web element.
  • We can use this variable to access the value in the enum. 
  • We have an interface that passes information By selector, since in WebDriver, selectors can pass the locators By id or By cssSelector, and we want to handle both types. To use this interface, ISelector, the LoginPageEnum is set up to implement ISelector. 
  • We even have array capturing the major web elements, in case we want to loop through them to see if the element .isDisplayed()


        String id;

        private LoginPageEnum(String Id){
            this.id = Id;
        }

        public String getId(){
            return id;
        }

        public By selector() {
            return By.cssSelector(getId());
        }

        public LoginPageEnum[] getDefaultElements(){
            return new LoginPageEnum[] {USERNAME, PASSWORD, LOGIN_BUTTON};
        }


For the interface ISelector:

package interfaces;

import org.openqa.selenium.By;

public interface ISelector {

        public By selector();
}

The source code for this interface can be found at test/java/interfaces/ISelector.java.

What is an interface?

"There are a number of situations in software engineering when it is important for disparate groups of programmers to agree to a "contract" that spells out how their software interacts. Each group should be able to write their code without any knowledge of how the other group's code is written. Generally speaking, interfaces are such contracts. 
"[...] In the Java programming language, an interface is a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods, and nested types. Method bodies exist only for default methods and static methods. Interfaces cannot be instantiated—they can only be implemented by classes or extended by other interfaces.  
"To use an interface, you write a class that implements the interface. When an instantiable class implements an interface, it provides a method body for each of the methods declared in the interface".
The Java Tutorials: Interfaces
Note that the LoginPageEnum implements the ISelector interface. 

How to use these selectors to find elements? 

Finding the text of the Header

Now that all the prep-work is complete, to find the HEADER selector, we can use ".selector()":

HEADER.selector().
If we wanted to create a method to grab the Header text, such as "Login Page" on the Login screen:

public String getHeaderText(){

System.out.println("Header is: " + getElementText(LoginPageEnum.HEADER.selector()));
        return getElementText(LoginPageEnum.HEADER.selector());

}

Finding the other web elements using Selectors


Likewise, in order to find the selectors for the web elements, all we need to do is to import LoginPageEnum, and then use:


  • LoginPageEnum.USERNAME.selector()
  • LoginPageEnum.PASSWORD.selector()
  • LoginPageEnum.LOGIN_BUTTON.selector()


NEXT: Page Object Model





-T.J. Maher
 Sr. QA Engineer, Fitbit
 Boston, MA

// Automated tester for [ 4 ] month and counting!

Please note: 'Adventures in Automation' is a personal blog about automated testing. It is not an official blog of Fitbit.com


Post a Comment