Program to an interface with Selenium WebDriver

pexels-photo-755834.jpeg

Be Flexible With Java Interfaces – Pexels.com

 

Anyone can write code that a computer can understand.
Good programmers write code that humans can understand.

Martin Fowler

 

Testers and developers use the Selenium WebDriver library for implementing test automation for various websites.

They use Selenium WebDriver for its major purpose: to interact with a site in the browser.

 

But I believe that there is something else that we can use this library for.

We can use it to learn how to write good code.

Code that is easy to maintain.

Easy to extend, easy to use and understand.

 

What can you learn from the Selenium WebDriver code?

 

You can learn how to apply programming concepts such as factories.

Or how to extend code instead of modifying it with decorators.

Or how program to an interface.

 

Programming to an interface is the focus here.

 

What does it mean?

 

It means two things:

1. each class that you create should implement an interface

2. every time an object of the class is created, its type should be the interface that the class implements

 

This sounds pretty abstract so lets see if an example makes things simpler.

 

This is the example provided for interfaces in the java documentation.

In its most common form, an interface is a group of related methods with empty bodies.

A bicycle’s behavior, if specified as an interface, might appear as follows:

 

interface Bicycle {

  void changeCadence(int newValue);

  void changeGear(int newValue);

  void speedUp(int increment);

  void applyBrakes(int decrement);
}

 

To implement this interface, the name of the class would change to a particular brand of bicycle and you’d use the implements keyword in the class declaration.

The class would also have to provide an implementation for each method defined by the interface.
class TrekBicycle implements Bicycle {

  int cadence = 0;
  int speed = 0;
  int gear = 1;

  void changeCadence(int newValue) {
    cadence = newValue;
  }

  void changeGear(int newValue) {
    gear = newValue;
  }

  void speedUp(int increment) {
    speed = speed + increment;
  }

  void applyBrakes(int decrement) {
    speed = speed - decrement;
  }

  void printStates() {
    System.out.println("cadence:" +
      cadence + " speed:" +speed + " gear:" + gear);
 }

}

 

Implementing an interface allows a class to become more formal about its behavior.

If your class claims to implement an interface, all methods defined by that interface must appear in its source code before the class will successfully compile.

 

What is so special about a class implementing an interface?

 

When you create an object of the class, you can do it in 2 different ways:

TrekBicycle trekBike = new TrekBicycle();
Bicycle trekBike = new TrekBicycle();

 

The second way of creating the object provides a lot of flexibility in dealing with all bicycle objects .

If you have an additional Bicycle class, for example

class GTBicycle implements Bicycle {

  int cadence = 0;
  int speed = 0;
  int gear = 1.25;

  void changeCadence(int newValue) {
    cadence = newValue;
  }

  void changeGear(int newValue) {
    gear = newValue * 1.1;
  }

  void speedUp(int increment) {
    speed = speed + increment + 1.2;
  }

  void applyBrakes(int decrement) {
    speed = speed - decrement;
  }

  void printStates() {
    System.out.println("cadence:" +cadence + " speed:" +
   speed + " gear:" + gear);
  }

}


an object of the GTBicycle class can also be saved in a Bicycle variable:

Bicycle gtBike = new TrekBicycle();

 

Any object of a class than implements the Bicycle interface can be saved in a Bicycle variable.

 

Where have we seen this with Selenium WebDriver?

 

You can see interfaces everywhere:

 

1. When creating a driver object

A driver object for the Chrome browser is created as follows

WebDriver driver = new ChromeDriver();

 

WebDriver is an interface while ChromeDriver is a class that extends RemoteDriver which implement WebDriver.

 

To create a driver for Firefox, we just change the class name:

WebDriver driver = new FirefoxDriver();

Same for the driver for Edge:

WebDriver driver = new EdgeDriver();

 

2. When finding a web element

WebElement button = driver.findElement(locator);

 

WebElement is an interface.

For this reason, you can save in a WebElement variable any type of html element: button, link, label, image, list, textbox, etc.

 

3. When typing a keyword in a textbox

All of the following ways of typing a keyword in a textbox work:

a. type a String value

WebElement searchBox = driver.findElement(textBoxId);
String keyword = "symphony";
searchBox.sendKeys(keyword);

 

b. type a StringBuilder value

WebElement searchBox = driver.findElement(textBoxId);
StringBuilder author = new StringBuilder()
                          .append("wolfgang")
                          .append(" ")
                          .append("amadeus")
                          .append(" ")
                          .append("mozart");

searchBox.sendKeys(author);

 

c. type a StringBuffer value

WebElement searchBox = driver.findElement(textBoxId);
StringBuffer topic = new StringBuffer().append("classical")
                                       .append(" ")
                                       .append("music");

searchBox.sendKeys(author);

 

d. type a String, a StringBuilder, a StringBuffer and a key

WebElement searchBox = driver.findElement(searchBoxId);
StringBuilder author = new StringBuilder()
                         .append("wolfgang")
                         .append(" ")
                         .append("amadeus")
                         .append(" ")
                         .append("mozart");

String space = " ";
StringBuffer topic = new StringBuffer()
                         .append("classical")
                         .append(" ")
                         .append("music");

String subTopic = "symphony";

searchBox.sendKeys(author, space, topic, space, subTopic, Keys.TAB);

 

Why do all these different ways of typing a keyword work?

 

It looks as if sendKeys may have one or more parameters and they can have different types: String, StringBuilder, StringBuffer, Keys.

The sendKeys signature explains it all:
void sendKeys(CharSequence... keysToSend)

 

The … explains why you can have one or more parameters.
… means that sendKeys can have a variable number of parameters of the same type.
All parameters must have the CharSequence type.

 

CharSequence explains why we can have so many classes as parameter types for sendKeys.

 

CharSequence is an interface in Java that is implemented by classes such as String, StringBuilder and StringBuffer.

This means that you can use any object of the String, StringBuffer and StringBuilder classes as a CharSequence variable.

 

What about Keys?

Keys is a Selenium enumeration which implements as well the CharSequence interface.

What should we learn from all this?

A few things …

1. Each class that we create should implement an interface.

2. When creating an object of the class, use the interface as its type.

 

How can you use interfaces in a Selenium project?

 

If you are interested in seeing how “programming to an interface” is done in a Selenium WebDriver project step by step, I have an ebook for you.

 

It will show you how to change code that does not use interfaces so that it follows the “programming to an interface” principle.

 

The result of applying this principle is that we will be able to replace 10 automated scripts that search for different categories (car, minivan, boat, rv, etc) with one script.

So, instead of having a search test for each category

public class TestClass {

   CarInfo carInfo = new CarInfo("audi", "a4");

   MinivanInfo minivanInfo = new MinivanInfo("nissan","navara", "pickup");

   BikesInfo bikesInfo = new BikesInfo(
                             "kawasaki", "gpz1000", "sports tourer");

   @Test
   public void canSearchForCars() {

     CarHome homePage = new CarHome();
     homePage.open();

     CarResults resultsPage = homePage.searchFor(carInfo);
     assertTrue(resultsPage.count() > 0);

   }

   @Test
   public void canSearchForMinivans() {

     MinivanHome homePage = new MinivanHome();
     homePage.open();

     MinivansResults resultsPage = homePage.searchFor(minivanInfo);
     assertTrue(resultsPage.count() > 0);

  }

  @Test
  public void canSearchForBikes() {

     BikesHome homePage = new BikesHome();
     homePage.open();

     BikesResults resultsPage = homePage.searchFor(bikesInfo);
     assertTrue(resultsPage.count() > 0);

  }


}

 

you can have

public class TestClass {

  String postalCode = "w1f7tu";

  @DataProvider(name = "vehicleInfo")
  public Object[][] vehicleInfo() {
    return new Object[][] {
      { "cars" , new CarInfo("BMW", "3 SERIES") },
      { "minivans", new MinivanInfo("NISSAN", "NAVARA", "Pickup")},
      { "bikes" , new MotorHomesInfo("AUTO-SLEEPERS", "2")},
    };
  }

  @Test(dataProvider = "vehicleInfo")
  public void canSearchForCategory(String category,
                                   VehicleInfo vehicleInfo) {

     HomePage homePage = openHomePage(category);
     homePage.open();

     ResultsPage resultsPage = homePage.searchFor(
                                     vehicleInfo, postalCode);
     assertTrue(resultsPage.count() > 0);
  }

}

 

Interested?

 

Send USD 15 to alex@alexsiminiuc.com through Paypal.
I will email you the book and answer all your questions about it, also by email.

 

Advertisements

The Beginner’s Guide To Explicit Waits

explicit wait flow

Test automation methods should synchronize with the web site every time they interact with elements.

For example, before clicking an element, the test method should make sure first that the element exists and that it is clickable.

Otherwise, an exception will be generated.

The synchronization is done using explicit waits and expected conditions.

Why do we need explicit waits?

Lets take the simplest Selenium WebDriver method:

driver.findElement(locator)

How does it work?

findElement() tries finding in the browser DOM the element matched by the locator.

If the element is found, findElement() returns it.

Otherwise, findElement() fails.

findElement() works well if the website is fast.

But if the website is slow and the element is not in the browser DOM when findElement() is executed, findElement() will fail.

We need a better way of interacting with website elements.

What are Explicit Waits?

Explicit wait objects are created for the WebDriverWait class.

Each wait object has 2 parameters:

  • the driver object
  • a timeout

First, we create the object:

WebDriverWait wait = new WebDriverWait(driver, timeout);

Then, we tell the wait object to wait until an expected condition is reached:

  • wait until the element is found and clickable
  • wait until the page title is correct
  • wait until the element is found and visible
  • wait until the page url matches a pattern

The waiting is being done by the until() method of the WebDriverWait class.

The until() method has a parameter as well which is the expected condition to wait for.

The expected condition is created using the ExpectedConditions class:

wait.until(ExpectedConditions.condition(parameter));

To explain how an explicit wait works, I will use the following example:

//create the id locator for the searchBox element
By searchBoxId = By.id("search-box");

//create the wait object
WebDriverWait wait = new WebDriverWait(driver, 10);

//find the searchBox element and save it in the WebElement variable
WebElement searchBoxElement = wait.until(
                                ExpectedConditions.
                                  elementToBeClickable
                                     (searchBoxId));

//type in the searchBox element
searchBoxElement.click();
searchBoxElement.clear();
searchBoxElement.sendKeys("java");

What does this code work?

  1. the locator of the searchBox element is created
  2. the wait object is created with the driver object and a 10 seconds timeout as parameters.
  3. the waiting for the searchBox element starts:
    1. until() method starts a timer.
    2. until() method verifies if searchBox is in the browser dom and is clickable
    3. if the condition is true (searchBox is in the browser dom and is clickable), the waiting ends and until() method returns the searchBox element
    4. if the condition is not met and the timer did not reach yet the timeout value, the code waits for 500 ms before continuing from step 3.2
  4. if until() method finds the element, it returns the searchBox element which is saved in the WebElement variable
  5. the code clicks the element, clears existing value and types the keyword in it
  6. if until() method cannot find the searchBox element within the 10 seconds timeout, it generates an exception; the remaining code is not executed

 

See below a simplified diagram that explains this process:

 

explicit wait

What can we do with explicit waits?

Explicit waits can be used for:

1. finding single web element

2. finding multiple web elements

3. checking the web page title and url

4. checking the element’s status

5. interacting with frames (not included in this article)

 

Find single web element

 

There are a few expected conditions that can help with finding a single web element.

1. elementToBeClickable

It defines an expectation for checking that an element is visible and enabled so that you can click it.

ExpectedCondition elementToBeClickable(By locator)
ExpectedCondition elementToBeClickable(WebElement element)

2. presenceOfElementLocated

It defines an expectation for checking that an element is present on the DOM of a page.

The element can be enabled or disabled.

Use this condition for checking for hidden elements.

ExpectedCondition presenceOfElementLocated(By locator)

3. visibilityOfElementLocated

ExpectedCondition visibilityOfElementLocated(By locator)

ExpectedCondition visibilityOf(WebElement element)

It defines an expectation for checking that an element is present on the DOM of a page and visible.

 

NOTE: All following examples are complete so that you can try them by yourself.

Example

The next code sample shows how the elementToBeClickable and visibilityOfElementLocated conditions are used for finding single elements.

It uses the Vancouver Public Library website.

It automates a test case that verifies if the search component works.

import org.junit.After;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class TestClass { 
  WebDriver driver; 
  WebDriverWait wait; 

  //this is where chromedriver.exe should be 
  String driverPath = "c:/browserdrivers/chromedriver.exe"; 

  @Before 
  public void setUp() { 
    System.setProperty("webdriver.chrome.driver"driverPath);           
    driver = new ChromeDriver(); 
    wait = new WebDriverWait(driver, 10); 
  } 

  @After 
  public void tearDown() { 
    driver.quit(); 
  } 
 
  @Test 
  public void clickElementWithExplicitWait() 
      throws InterruptedException { 
  
    //open site 
    driver.get("http://www.vpl.ca"); 

    //finds the search box using the elementToBeClickable condition
    By searchBoxId = By.id("edit-search"); 
    WebElement searchBox = wait.until(
                             ExpectedConditions
                               .elementToBeClickable
                                  (searchBoxId)); 

    //types in the search box 
    searchBox.click(); 
    searchBox.clear(); 
    searchBox.sendKeys("java"); 
    
    //finds search button using the visibilityOfElementLocated condition
    By searchButtonId = By.id("edit-submit"); 
    WebElement searchButton = wait.until(
                                ExpectedConditions.
                                  visibilityOfElementLocated
                                      (searchButtonId)); 
    
    //clicks the search button 
    searchButton.click();

    //delay so you can see whats happening in the browser 
    Thread.sleep(5000); 
  }
} 

 

find multiple web elements

There are a few expected conditions that can help with finding multiple web elements.

1. visibilityOfAllElementsLocatedBy

It defines an expectation for checking that all elements present on the web page that match the locator are visible.

ExpectedCondition<List> visibilityOfAllElementsLocatedBy(By locator)
ExpectedCondition<List> visibilityOfAllElements(List elements)

 

2. presenceOfAllElementsLocatedBy

It defines an expectation for checking that there is at least 1 element present on the page.

ExpectedCondition<List> presenceOfAllElementsLocatedBy(By locator)

Example

The next code sample continues the previous one.

After the search is done, on the results page, it checks that the

  • title count is 25
  • author count is > 0

It finds the title and author elements using the 2 expected conditions for finding multiple elements:

import static org.junit.Assert.*;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class TestClass { 
  WebDriver driver; 
  WebDriverWait wait; 

  //this is where chromedriver.exe should be 
  String driverPath = "c:/browserdrivers/chromedriver.exe"; 

  @Before 
  public void setUp() { 
    System.setProperty("webdriver.chrome.driver"driverPath); 
    driver = new ChromeDriver(); 
    wait = new WebDriverWait(driver, 10); 
  } 

  @After 
  public void tearDown() { 
    driver.quit(); 
  } 

  @Test 
  public void clickElementWithExplicitWait() 
    throws InterruptedException { 

    //open site 
    driver.get("http://www.vpl.ca"); 

    //finds the search box 
    By searchBoxId = By.id("edit-search"); 
    WebElement searchBox = wait.until
                              (ExpectedConditions
                                  .elementToBeClickable
                                     (searchBoxId)); 

    //types in the search box 
    searchBox.click(); 
    searchBox.clear(); 
    searchBox.sendKeys("java"); 

    //finds the search button 
    By searchButtonId = By.id("edit-submit"); 
    WebElement searchButton = wait.until
                               (ExpectedConditions
                                 .visibilityOfElementLocated
                                   (searchButtonId)); 

    //clicks the search button 
    searchButton.click(); 

    //delay so you can see whats happening in the browser 
    Thread.sleep(10000); 

    //find all titles using visibilityOfAllElementsLocatedBy 
    By titleLocator = By.xpath("//a[@testid = 'bib_link']"); 
    List<WebElement> titles = wait.until
                               (ExpectedConditions.
                                  visibilityOfAllElementsLocatedBy
                                    (titleLocator)); 

    //get the number of titles found 
    int titleCount = titles.size();

    //check that the title count is equal to 25 
    assertEquals(titleCount, 25); 
    System.out.println("title count = " + titleCount);

    //find all authors using presenceOfAllElementsLocatedBy 
    By authorLocator = By.xpath("//a[@testid = 'author_search']"); 
    List<WebElement> authors = wait.until(
                                 ExpectedConditions
                                   .presenceOfAllElementsLocatedBy
                                     (authorLocator)); 

    //get the number of authors found 
    int authorCount = authors.size();
  
    //check that the author count is > 0 
    assertTrue(authorCount > 0); 
    System.out.println("author count = " + authorCount);
 }
}

 

 

check web page title and url

The following expected conditions can be used for checking the web page title and url.

1. titleContains

It defines an expectation for checking that the title contains a case-sensitive substring.

ExpectedCondition titleContains(java.lang.String title)

2. titleIs

ExpectedCondition titleIs(java.lang.String title)

It defines an expectation for checking the title of a page.

3. urlContains

ExpectedCondition urlContains(java.lang.String fraction)

It defines an expectation for the URL of the current page to contain specific text.

4. urlToBe

It defines an expectation for the URL of the current page to be a specific url.

ExpectedCondition urlToBe(java.lang.String url)

5. urlMatches

ExpectedCondition urlMatches(java.lang.String regex)

It defines an expectation for the URL to match a specific regular expression

 

Example

Let’s improve the first code sample by checking that the page titles and urls are correct.

import static org.junit.Assert.*;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class TestClass { 
  WebDriver driver; 
  WebDriverWait wait; 

  //this is where chromedriver.exe should be 
  String driverPath = "c:/browserdrivers/chromedriver.exe"; 

  @Before 
  public void setUp() { 
    System.setProperty("webdriver.chrome.driver",            
                      driverPath); 
    driver = new ChromeDriver(); 
    wait = new WebDriverWait(driver, 10); 
  } 

  @After 
  public void tearDown() { 
    driver.quit(); 
  } 

  @Test 
  public void clickElementWithExplicitWait() 
       throws InterruptedException { 

    //open site 
    driver.get("http://www.vpl.ca"); 

    //check if the home title is correct String 
    expectedHomeTitle = "Vancouver Public Library |"; 
    boolean isTitleCorrect = wait.until 
                                (ExpectedConditions .
                                   titleIs(expectedHomeTitle)); 
    assertTrue(isTitleCorrect == true); 

    //finds the search box 
    By searchBoxId = By.id("edit-search"); 
    WebElement searchBox = wait.until(
                               ExpectedConditions .
                                 elementToBeClickable(searchBoxId)); 

    //types in the search box 
    searchBox.click(); 
    searchBox.clear(); 
    searchBox.sendKeys("java"); 

    //finds the search button 
    By searchButtonId = By.id("edit-submit"); 
    WebElement searchButton = wait.until(
                                 ExpectedConditions .
                                    visibilityOfElementLocated 
                                        (searchButtonId)); 

    //clicks the search button 
    searchButton.click(); 

    //check if the results url is correct 
    String expectedResultsUrl = "https://vpl.bibliocommons.com/search"; 
    boolean isUrlCorrect = wait.until (
                             ExpectedConditions .
                                urlContains(expectedResultsUrl)); 
    assertTrue(isUrlCorrect == true); 

    //delay so you can see whats happening in the browser 
    Thread.sleep(10000); 
 }
}

check elements status

All previous expected conditions are very common.

There are also other expected conditions that can be used but are less common.

No complete code samples are provided for them but feel free to try them out.

1. elementSelectionStateToBe

ExpectedCondition elementSelectionStateToBe(By locator, boolean selected)
ExpectedCondition elementSelectionStateToBe(WebElement element, boolean selected)

It defines an expectation for checking if the given element is selected.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      elementSelectionStateToBe(locator, true)));

2. elementToBeSelected

ExpectedCondition elementToBeSelected(By locator)
ExpectedCondition elementToBeSelected(WebElement element)

It defines an expectation for checking if the given element is selected.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      elementToBeSelected(locator)));

3. invisibilityOfElementLocated

ExpectedCondition invisibilityOfElementLocated(By locator)

It defines an expectation for checking that an element is either invisible or not present on the DOM.

Example

 

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      invisibilityOfElementLocated(locator)));

4. invisibilityOfElementWithText

ExpectedCondition invisibilityOfElementWithText(By locator, java.lang.String text)

It defines an expectation for checking that an element with text is either invisible or not present on the DOM.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      invisibilityOfElementWithText(locator, text)));

5. stalenessOf

ExpectedCondition stalenessOf(WebElement element)

Wait until an element is no longer attached to the DOM.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      stalenessOf(element)));

6. textToBePresentInElement

ExpectedCondition textToBePresentInElement(WebElement element, java.lang.String text)
ExpectedCondition textToBePresentInElementLocated(By locator, java.lang.String text)

It defines an expectation for checking if the given text is present in the element that matches the given locator.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      textToBePresentInElementLocated(locator, keyword)));

7. textToBePresentInElementValue

ExpectedCondition textToBePresentInElementValue(By locator, java.lang.String text)
ExpectedCondition textToBePresentInElementValue(WebElement element, java.lang.String text)

It defines an expectation for checking if the given text is present in the specified elements value attribute.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      textToBePresentInElementValue(locator, keyword)));

 

RELATED ARTICLE

How to create custom expected conditions in Selenium