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."
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 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; } }
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;
}
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 */
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 LabsoptPrefs
: Map of driver preferences (this will be covered later in detail)
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
: ThesetDriver
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
andgetCurrentDriver
: ThegetDriver
andgetCurrentDriver
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
anddriverRefresh
: ThedriverWait
method will "pause" the script for the designated amount of time in seconds, although this should not be used to synchronize event handling. ThedriverRefresh
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
: ThecloseDriver
method will retrieve the current driver and call the WebDriver'squit
method on it, browser or mobile:
/** * closeDriver method quits the current active driver */ public void closeDriver() { try { getCurrentDriver().quit(); } catch ( Exception e ) { // do something } }