Book Image

Mastering Selenium WebDriver 3.0 - Second Edition

Book Image

Mastering Selenium WebDriver 3.0 - Second Edition

Overview of this book

The second edition of Mastering Selenium 3.0 WebDriver starts by showing you how to build your own Selenium framework with Maven. You'll then look at how you can solve the difficult problems that you will undoubtedly come across as you start using Selenium in an enterprise environment and learn how to produce the right feedback when failing. Next, you’ll explore common exceptions that you will come across as you use Selenium, the root causes of these exceptions, and how to fix them. Along the way, you’ll use Advanced User Interactions APIs, running any JavaScript you need through Selenium; and learn how to quickly spin up a Selenium Grid using Docker containers. In the concluding chapters, you‘ll work through a series of scenarios that demonstrate how to extend Selenium to work with external libraries and applications so that you can be sure you are using the right tool for the job.
Table of Contents (15 chapters)

Building our test project with Apache Maven

Getting a full working Maven install up and running is not within the scope of this book. It's okay though, don't panic! The Apache Software Foundation has you covered, it has a guide to setting up Maven up in just five minutes! See the following link:

http://maven.apache.org/guides/getting-started/maven-in-five-minutes.html

If you are running the Debian derivative of Linux, it is as easy as using this command:

sudo apt-get install maven 

Or if you are running a Mac with Homebrew, it is just this code:

brew install maven 

Once you have Maven installed and working, we will start our Selenium project with a basic POM file. We are going to start by creating a basic Maven directory structure and then creating a file called pom.xml in it. Take a look at the following screenshot:

There are two main testing frameworks that you will come across in a Java environment; JUnit and TestNG. I personally find TestNG to be easier to get up and running out of the box, but I find JUnit to be more extensible. TestNG certainly seems to be popular on the Selenium mailing list, with many threads asking questions about it; you don't often see JUnit questions any more.

I'm not going to suggest either one as the right choice, as they are both capable frameworks that you will probably come across in the enterprise world. Since TestNG seems to be the more popular option, we will focus on a TestNG implementation in this chapter.

If you prefer JUnit, you will want to have a look at Appendix 2Working with jUnit. In this, we will implement the same base project, but we will use JUnit instead of TestNG. This means instead of worrying about which one is best, you can have a look at a TestNG implementation and a JUnit implementation. You can then choose which one you prefer and read the relevant section.

So, to start with, let's have a look at a basic POM code for a TestNG-based Maven project:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"
>

<groupId>com.masteringselenium.demo</groupId>
<artifactId>mastering-selenium-testng</artifactId>
<version>DEV-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>

<name>Mastering Selenium TestNG</name>
<description>A basic Selenium POM file</description>
<url>http://www.masteringselenium.com</url>

<properties>
<project.build.sourceEncoding>UTF-
8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-
8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- Dependency versions -->
<selenium.version>3.12.0</selenium.version>
<testng.version>6.14.3</testng.version>
<!-- Plugin versions -->
<maven-compiler-plugin.version>3.7.0</maven-compiler-
plugin.version>
<maven-failsafe-plugin.version>2.21.0</maven-failsafe-
plugin.version>
<!-- Configurable variables -->
<threads>1</threads>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
<version>${maven-compiler-plugin.version}</version>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
<version>${selenium.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>

What you are seeing here is mainly Maven boilerplate code. The groupId, artifactId, and version are subject to standard naming conventions, which are as follows :

  • groupId should be a domain that you own/control and is entered in reverse
  • artifactId is the name that will be allocated to your JAR file, so remember to make it what you want your JAR file to be called
  • version should always be a number with -SNAPSHOT appended to the end; this shows that it is currently a work in process

We have added the Maven compiler plugin so that we can define the version of Java that is required to compile the code we are going to write. We have opted to require Java 8, since this is the minimum version of Java currently supported by Selenium.

Next, we have the libraries that our code depends upon; these are stored in the dependencies block. To get us started, we have added a dependency for Selenium and a dependency for TestNG. Note that we have given them a scope of test; this ensures that these dependencies are only loaded into the classpath when tests are run and are never packaged up in any artifacts that are generated as part of the build process.

We have used Maven properties to set our dependency versions. This is not required, but it's a common Maven convention. The idea is that it's easier to update the versions of things in your POM if they are all declared in one place. XML can be very verbose and wading through the POM finding each dependency or plugin version that we want to update can be time consuming, especially when you start using Maven profiles.

You can now open up this POM file using your IDE (in this book, I'm assuming that you are using IntelliJ IDEA, but any modern IDE should be able to open up a POM file and create a project from it).

We now have the basis of our Selenium project. The next step is to create a basic test that we can run using Maven. Start by creating a src/test/java directory. Your IDE should automatically work out that this directory is a test sources directory. We then need to create a new package in this directory called com.masteringselenium. Finally, inside this package, we will create a file called BasicTest.java. Into this file we are going to put the following code:

package com.masteringselenium;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.Test;

public class BasicTest {

private ExpectedCondition<Boolean> pageTitleStartsWith(final
String searchString) {
return driver -> driver.getTitle().toLowerCase().
startsWith(searchString.toLowerCase());
}

private void googleExampleThatSearchesFor(final
String searchString) {

WebDriver driver = new FirefoxDriver();

driver.get("http://www.google.com");

WebElement searchField = driver.findElement(By.name("q"));

searchField.clear();
searchField.sendKeys(searchString);

System.out.println("Page title is: " + driver.getTitle());

searchField.submit();

WebDriverWait wait = new WebDriverWait(driver, 10, 100);
wait.until(pageTitleStartsWith(searchString));

System.out.println("Page title is: " + driver.getTitle());

driver.quit();
}

@Test
public void googleCheeseExample() {
googleExampleThatSearchesFor("Cheese!");
}

@Test
public void googleMilkExample() {
googleExampleThatSearchesFor("Milk!");
}
}

These two tests should be quite familiar; it's the basic Google cheese scenario with all the main grunt work abstracted out into a method that we are able to call multiple times with different search terms. We now have everything we need to run our tests. To kick them off, type the following command into a Terminal window:

mvn clean verify 

You will now see Maven downloading all of the Java dependencies from Maven central. When it is completed, it will build the project and then run the tests.

If you have problems downloading the dependencies, try adding a -U to the end of the command; this will force Maven to check the Maven central repositories for updated libraries.

You will now see Firefox load up and then your test will fail, since with Selenium 3 all of the driver binaries (the part that actually drives the browser) are no longer bundled with Selenium. You will now have to download the relevant binaries to be able to run your tests.

For now, we will download a binary and then pass an environmental variable into the JVM so that we can get this initial test running. Later on, we will take a look at a slightly more streamlined way to do this that will automatically download the required driver binaries.

We are running our tests against Firefox, so we will need to download the geckodriver binary; the latest one is available at https://github.com/mozilla/geckodriver/releases.

Now that we have a usable driver binary, we need to tell Selenium where to find it. Luckily, the Selenium team have already provided us with a way to do this. When Selenium starts up and tries to instantiate a driver object, it will look for a system property that holds the location of the required executable. These system properties are in the format WebDriver.<DRIVER_TYPE>.driver. To get our test working, all we need to do is pass this system property on the command line:

mvn clean verify -Dwebdriver.gecko.driver=<PATH_TO_GECKODRIVER_BINARY>

This time, Firefox should load up correctly, run your tests without any error, and finally give you a pass.

If you are still having problems, check the version of Firefox that you are using. The code in this chapter has been written against Firefox 60. If you are using earlier versions, you may suffer from patchy geckodriver support, and you may see some errors.

We now have a very basic project set up to run a couple of very basic tests using Maven. Right now, this will run very quickly, but as you start adding more and more tests to your project, things are going to start to slow down. To try and mitigate this problem, we are going to utilize the full power of your machine by running your tests in parallel.