Book Image

Selenium Framework Design in Data-Driven Testing

By : Carl Cocchiaro
Book Image

Selenium Framework Design in Data-Driven Testing

By: Carl Cocchiaro

Overview of this book

The Selenium WebDriver 3.x Technology is an open source API available to test both Browser and Mobile applications. It is completely platform independent in that tests built for one browser or mobile device, will also work on all other browsers and mobile devices. Selenium supports all major development languages which allow it to be tied directly into the technology used to develop the applications. This guide will provide a step-by-step approach to designing and building a data-driven test framework using Selenium WebDriver, Java, and TestNG. The book starts off by introducing users to the Selenium Page Object Design Patterns and D.R.Y Approaches to Software Development. In doing so, it covers designing and building a Selenium WebDriver framework that supports both Browser and Mobile Devices. It will lead the user through a journey of architecting their own framework with a scalable driver class, Java utility classes, JSON Data Provider, Data-Driven Test Classes, and support for third party tools and plugins. Users will learn how to design and build a Selenium Grid from scratch to allow the framework to scale and support different browsers, mobile devices, versions, and platforms, and how they can leverage third party grids in the Cloud like SauceLabs. Other topics covered include designing abstract base and sub-classes, inheritance, dual-driver support, parallel testing, testing multi-branded applications, best practices for using locators, and data encapsulation. Finally, you will be presented with a sample fully-functional framework to get them up and running with the Selenium WebDriver for browser testing. By the end of the book, you will be able to design your own automation testing framework and perform data-driven testing with Selenium WebDriver.
Table of Contents (15 chapters)
Title Page
Dedication
Packt Upsell
Contributors
Preface

The singleton driver class


In this section, a Java singleton class will be used to create the driver class. This will force the user to use the same object for all instances where the WebDriver is required. The WebDriver events will never get out of sync during the run, and all WebDriver events will get sent to the correct browser or mobile device instance. And since the instance of the class is created on a single thread, referencing it won't interfere with other WebDriver instances running on the same node simultaneously.

As per Wikipedia (https://en.wikipedia.org/wiki/Singleton_pattern):

"In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects. The term comes from the mathematical concept of a singleton."

Requirements

In order to start building the framework, users must import the required JAR files into their project to use the Selenium WebDriver, AppiumDriver, and TestNG APIs. Additionally, there will be various Java JAR files required, such as Apache, Spring, File I/O, and other utilities as the framework develops:

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.*;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.LocalFileDetector;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.safari.SafariDriver;
import org.openqa.selenium.safari.SafariOptions;
import org.testng.*;

Note

A good source location for finding these JAR files is https://mvnrepository.com/.

The class signature

The class should be named something obvious such as Driver.java, CreateDriver.java, SeleniumDriver.java, and so on. Since this will be a Java singleton class, it will contain a private constructor and a static getInstance method as follows:

/**
 * Selenium Singleton Class
 *
 * @author CarlCocchiaro
 *
 */
@SuppressWarnings("varargs")
public class CreateDriver {

    // constructor
    private CreateDriver() {
    }

    /**
     * getInstance method to retrieve active driver instance
     *
     * @return CreateDriver
     */
    public staticCreateDriver getInstance() {
        if ( instance == null ) {
            instance = new CreateDriver();
        }

        return instance;
    }
}

Class variables

Initially, when building the class, there will be various private and public variables used that should be declared at the top of the class. This organizes the variables into one place in the file, but of course, this is a coding style guideline. Some of the common variables required to start are as follows:

public class CreateDriver {
    // local variables
    private static CreateDriver instance = null;
    private String browserHandle = null;
    private static final int IMPLICIT_TIMEOUT = 0;

    private ThreadLocal<WebDriver> webDriver =
            new ThreadLocal<WebDriver>();

    private ThreadLocal<AppiumDriver<MobileElement>> mobileDriver =
            new ThreadLocal<AppiumDriver<MobileElement>>();

    private ThreadLocal<String> sessionId =
            new ThreadLocal<String>();

    private ThreadLocal<String> sessionBrowser =
            new ThreadLocal<String>();

    private ThreadLocal<String> sessionPlatform =
            new ThreadLocal<String>();

    private ThreadLocal<String> sessionVersion =
            new ThreadLocal<String>();

    private String getEnv = null;
}

JavaDoc

Before introducing the common methods in this driver class, it is prudent to note that requiring JavaDoc for all methods in the class will be helpful for users who are learning the framework. The JavaDoc can be built automatically in Java using a build tool such as Maven, Gradle, or Ant. An example of the JavaDoc format is as follows:

/**
 * This is the setDriver method used to create the Selenium WebDriver
 * or AppiumDriver instance!
 *
 * @param parameter 1
 * @param parameter 2
 * @param parameter 3
 * @param parameter 4
 *
 * @throws Exception
 */

Parameters

The driver class will be designed with various get and set methods. The main setDriver method can take parameters to determine the browser or mobile type, platform to run on, environment for testing, and a set of optional preferences to allow changing driver behavior on the fly:

@SafeVarargs
public final void setDriver(String browser, 
                            String platform,
                            String environment,
                            Map<String, Object>... optPreferences)

Examples of some of the parameters of setDriver are as follows:

  • browser: Chrome, Firefox, Internet Explorer, Microsoft Edge, Opera, Safari (iPhone/iPad, or Android for mobile)
  • platform: Linux, Windows, Mac, Sierra, Win10 (iPhone/iPad, or Android for mobile)
  • environment: Local, remote, and Sauce Labs
  • optPrefs: Map of driver preferences (this will be covered later in detail)

Class methods

All the methods in this class should pertain to the web or mobile driver. This includes things such as setDriver, getDriver, getCurrentDriver, getSessionID/Browser/Version/Platform, driverWait, driverRefresh, and closeDriver. Each will be outlined in this section:

  • setDriver: The setDriver methods (standard and overloaded) will allow users to create a new instance of the driver for testing browser or mobile devices. The method will take parameters for browser, platform, environment, and optional preferences. Based on these preferences, the WebDriver/AppiumDriver of choice will be created. Here are some key points of the method, including a code sample:
  • The driver preferences are set up using the DesiredCapabilities class
  • The method will be segregated according to the browser or mobile type, platform, and environment
  • The method will be overloaded to allow switching back and forth between multiple drivers running concurrently

The following code demonstrates the standard setDriver method:

/**
 * setDriver method
 *
 * @param browser
 * @param environment
 * @param platform
 * @param optPreferences
 * @throws Exception
 */
@SafeVarargs
public final void setDriver(String browser,
                            String environment,
                            String platform,
                            Map<String, Object>... optPreferences)
                            throws Exception {

    DesiredCapabilities caps = null;
    String localHub = "http://127.0.0.1:4723/wd/hub";
    String getPlatform = null;

    switch (browser) {
        case "firefox":
            caps = DesiredCapabilities.firefox();
            webDriver.set(new FirefoxDriver(caps));

            break;
        case "chrome":
            caps = DesiredCapabilities.chrome();
            webDriver.set(new ChromeDriver(caps));

            break;
        case "internet explorer":
            caps = DesiredCapabilities.internetExplorer();
            webDriver.set(new 
                          InternetExplorerDriver(caps));

            break;
        case "safari":
            caps = DesiredCapabilities.safari();
            webDriver.set(new SafariDriver(caps));

            break;
        case "microsoftedge":
            caps = DesiredCapabilities.edge();
            webDriver.set(new EdgeDriver(caps));

            break;
        case "iphone":
        case "ipad":
            if (browser.equalsIgnoreCase("ipad")) {
                caps = DesiredCapabilities.ipad();
            }

            else {
                caps = DesiredCapabilities.iphone();
            }

            mobileDriver.set(new IOSDriver<MobileElement>(
                             new URL(localHub), caps));

            break;
        case "android":
            caps = DesiredCapabilities.android();
            mobileDriver.set(new 
                             AndroidDriver<MobileElement>(
                             new URL(localHub), caps));

            break;
    }
}

Here is the overloaded setDriver method:

/** 
 * overloaded setDriver method to switch driver to specific WebDriver
 * if running concurrent drivers
 *
 * @param driver WebDriver instance to switch to
 */
public void setDriver(WebDriver driver) {
    webDriver.set(driver);

    sessionId.set(((RemoteWebDriver) webDriver.get())
    .getSessionId().toString());

    sessionBrowser.set(((RemoteWebDriver) webDriver.get())
    .getCapabilities().getBrowserName());

    sessionPlatform.set(((RemoteWebDriver) webDriver.get())
    .getCapabilities().getPlatform().toString());

    setBrowserHandle(getDriver().getWindowHandle());
}

/**
 * overloaded setDriver method to switch driver to specific AppiumDriver
 * if running concurrent drivers
 *
 * @param driver AppiumDriver instance to switch to
 */
public void setDriver(AppiumDriver<MobileElement> driver) {
    mobileDriver.set(driver);

    sessionId.set(mobileDriver.get()
    .getSessionId().toString());

    sessionBrowser.set(mobileDriver.get()
    .getCapabilities().getBrowserName());

    sessionPlatform.set(mobileDriver.get()
    .getCapabilities().getPlatform().toString());
}
  • getDriver and getCurrentDriver: The getDriver and getCurrentDriver methods (standard and overloaded) will allow users to retrieve the current driver, whether that be browser or mobile. The driver should be instantiated at the beginning of the test, and will remain available throughout the test by using these methods. Since many of the Selenium WebDriver methods require the driver to be passed to it, these methods will allow users to retrieve the currently active session:
/**
 * getDriver method will retrieve the active WebDriver
 *
 * @return WebDriver
 */
public WebDriver getDriver() {
    return webDriver.get();
}

/**
 * getDriver method will retrieve the active AppiumDriver
 *
 * @param mobile boolean parameter
 * @return AppiumDriver
 */
public AppiumDriver<MobileElement> getDriver(boolean mobile) {
    return mobileDriver.get();
}

/**
 * getCurrentDriver method will retrieve the active WebDriver
 * or AppiumDriver
 *
 * @return WebDriver
 */
public WebDriver getCurrentDriver() {
    if ( getInstance().getSessionBrowser().contains("iphone") ||
         getInstance().getSessionBrowser().contains("ipad") ||
         getInstance().getSessionBrowser().contains("android") ) {

        return getInstance().getDriver(true);
    }

    else {
        return getInstance().getDriver();
    }
}
  • driverWait and driverRefresh: The driverWait method will "pause" the script for the designated amount of time in seconds, although this should not be used to synchronize event handling. The driverRefresh method will reload the currently active browser page:
/**
 * driverWait method pauses the driver in seconds
 *
 * @param seconds to pause
 */
public void driverWait(long seconds) {
    try {
        Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));
    }

    catch (InterruptedException e) {
        // do something
    }
}

/**
 * driverRefresh method reloads the current browser page
 */
public void driverRefresh() {
    getCurrentDriver().navigate().refresh();
}
  • closeDriver: The closeDriver method will retrieve the current driver and call the WebDriver's quit method on it, browser or mobile:
/**
 * closeDriver method quits the current active driver
 */
public void closeDriver() {
    try {
        getCurrentDriver().quit();
    }

    catch ( Exception e ) {
        // do something
    }
}