Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
Mastering Selenium WebDriver 3.0

You're reading from   Mastering Selenium WebDriver 3.0 Boost the performance and reliability of your automated checks by mastering Selenium WebDriver

Arrow left icon
Product type Paperback
Published in Jun 2018
Publisher Packt
ISBN-13 9781788299671
Length 376 pages
Edition 2nd Edition
Languages
Tools
Arrow right icon
Toc

Table of Contents (19) Chapters Close

Title Page
Contributors
Packt Upsell
Preface
1. Creating a Fast Feedback Loop FREE CHAPTER 2. Producing the Right Feedback When Failing 3. Exceptions Are Actually Oracles 4. The Waiting Game 5. Working with Effective Page Objects 6. Utilizing the Advanced User Interactions API 7. JavaScript Execution with Selenium 8. Keeping It Real 9. Hooking Docker into Selenium 10. Selenium – the Future 1. Appendix A: Contributing to Selenium 2. Appendix B: Working with JUnit 3. Appendix C: Introduction to Appium 4. Other Books You May Enjoy Index

Switching from TestNG to JUnit


First of all, we need to make some changes to our POM.xml to use JUnit instead of TestNG; we will start with the properties block:

<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>
    <junit.version>4.12</junit.version>
    <assertj-core.version>3.10.0</assertj-core.version>
    <query.version>1.2.0</query.version>
    <commons-io.version>2.6</commons-io.version>
    <httpclient.version>4.5.5</httpclient.version>
<!-- Plugin versions -->
<driver-binary-downloader-maven-plugin.version>1.0.17</driver-  
    binary-downloader-maven-plugin.version>
    <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>
    <browser>firefox</browser>
    <overwrite.binaries>false</overwrite.binaries>
    <headless>true</headless>
    <remote>false</remote>
    <seleniumGridURL/>
    <platform/>
    <browserVersion/>
    <screenshotDirectory>${project.build.directory}
    /screenshots</screenshotDirectory>
    <proxyEnabled>false</proxyEnabled>
    <proxyHost/>
    <proxyPort/>
</properties>

Then we need to modify our dependencies.  We are going to remove the testNG dependency and instead add in a Unit dependency:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
</dependency>

For the final changes to our POM.xml, we need to modify our plugin configuration:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>${maven-failsafe-plugin.version}</version>
    <configuration>
        <parallel>methods</parallel>
        <threadCount>${threads}</threadCount>
        <perCoreThreadCount>false</perCoreThreadCount>
        <properties>
            <property>
                <name>listener</name>
                <value>com.masteringselenium.
                listeners.ScreenshotListener</value>
            </property>
        </properties>
        <systemPropertyVariables>
            <browser>${browser}</browser>
            <headless>${headless}</headless>
            <remoteDriver>${remote}</remoteDriver>
            <gridURL>${seleniumGridURL}</gridURL>
            <desiredPlatform>${platform}</desiredPlatform>
            <desiredBrowserVersion>${browserVersion}
            </desiredBrowserVersion>
            <screenshotDirectory>${screenshotDirectory}
            </screenshotDirectory>
            <proxyEnabled>${proxyEnabled}</proxyEnabled>
            <proxyHost>${proxyHost}</proxyHost>
            <proxyPort>${proxyPort}</proxyPort>
<!--Set properties passed in by the driver binary 
            downloader-->
<webdriver.chrome.driver>${webdriver.chrome.driver} 
            </webdriver.chrome.driver>
            <webdriver.ie.driver>${webdriver.ie.driver}
            </webdriver.ie.driver>
            <webdriver.opera.driver>${webdriver.opera.driver}
            </webdriver.opera.driver>
            <webdriver.gecko.driver>${webdriver.gecko.driver}
            </webdriver.gecko.driver>
            <webdriver.edge.driver>${webdriver.edge.driver}
            </webdriver.edge.driver>
        </systemPropertyVariables>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>integration-test</goal>
                <goal>verify</goal>
            </goals>
        </execution>
    </executions>
</plugin>

The first change is to add the <perCoreThreadCount>false</perCoreThreadCount> configuration setting. When using the surefire plugin with JUnit, the plugin applies the thread count to each CPU core you have in your machine. We want to make sure that we are supplying a total number of browsers, not defaulting to eight browsers if you have an eight-core machine.

The second change is to specify the location of our ScreenshotListener class; we can't apply it using an annotation in code, like we did with TestNG. As a result, we are using the Maven Failsafe plugin configuration block to apply it instead.

Now we need to make some changes to our DriverBase class:

package com.masteringselenium;

import com.masteringselenium.config.DriverFactory;
import net.lightbody.bmp.BrowserMobProxy;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.openqa.selenium.remote.RemoteWebDriver;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class DriverBase {

private static List<DriverFactory> webDriverThreadPool =  
    Collections.synchronizedList(new ArrayList<DriverFactory>());
    private static ThreadLocal<DriverFactory> driverThread;

@BeforeClass
public static void instantiateDriverObject() {
driverThread = new ThreadLocal<DriverFactory>() {
@Override
protected DriverFactory initialValue() {
                DriverFactory webDriverThread = new DriverFactory();
webDriverThreadPool.add(webDriverThread);
                return webDriverThread;
}
        };
}

public static RemoteWebDriver getBrowserMobProxyEnabledDriver() 
    throws MalformedURLException {
return driverThread.get().getDriver(true);
}

public static RemoteWebDriver getDriver() throws 
    MalformedURLException {
return driverThread.get().getDriver();
}

public static BrowserMobProxy getBrowserMobProxy() {
return driverThread.get().getBrowserMobProxy();
}

@After
public void clearCookies() {
try {
getDriver().manage().deleteAllCookies();
} catch (Exception ex) {
            System.err.println("Unable to delete cookies: " + ex);
}
    }

@AfterClass
public static void closeDriverObjects() {
for (DriverFactory webDriverThread : webDriverThreadPool) {
            webDriverThread.quitDriver();
}
    }
}

There aren't many changes here. We have switched out the TestNG annotations for JUnit ones, and we have removed the @Listener annotation because JUnit doesn't have an equivalent. This does mean that whilst running our tests through an IDE, we will no longer get screenshots on failure. Don't worry though, it will still work when running the build on the command line using Maven. You will still be able to collect screenshots on your CI server if things go wrong.

Finally, we need to modify our ScreenshotListener class to work with JUnit instead of TestNG:

package com.masteringselenium.listeners;

import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.Augmenter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import static com.masteringselenium.DriverBase.getDriver;


public class ScreenshotListener extends RunListener {

private boolean createFile(File screenshot) {
boolean fileCreated = false;

        if (screenshot.exists()) {
            fileCreated = true;
} else {
            File parentDirectory = new File(screenshot.getParent());
            if (parentDirectory.exists() || parentDirectory.mkdirs()) {
try {
                    fileCreated = screenshot.createNewFile();
} catch (IOException errorCreatingScreenshot) {
                    errorCreatingScreenshot.printStackTrace();
}
            }
        }

return fileCreated;
}

private void writeScreenshotToFile(WebDriver driver, File  
    screenshot) {
try {
            FileOutputStream screenshotStream = new 
            FileOutputStream(screenshot);
screenshotStream.write(((TakesScreenshot)  
            driver).getScreenshotAs(OutputType.BYTES));
screenshotStream.close();
} catch (IOException unableToWriteScreenshot) {
            System.err.println("Unable to write " + 
            screenshot.getAbsolutePath());
unableToWriteScreenshot.printStackTrace();
}
    }

@Override
public void testFailure(Failure failure) {
try {
            WebDriver driver = getDriver();
String screenshotDirectory =  
            System.getProperty("screenshotDirectory", 
            "target/screenshots");
String screenshotAbsolutePath = screenshotDirectory +  
            File.separator + System.currentTimeMillis() + "_" + 
            failure.getDescription().getMethodName() + ".png";
File screenshot = new File(screenshotAbsolutePath);
            if (createFile(screenshot)) {
try {
                    writeScreenshotToFile(driver, screenshot);
} catch (ClassCastException  
                weNeedToAugmentOurDriverObject) {
                    writeScreenshotToFile(new 
                    Augmenter().augment(driver), screenshot);
}
                System.out.println("Written screenshot to " + 
                screenshotAbsolutePath);
} else {
                System.err.println("Unable to create " + 
                screenshotAbsolutePath);
}
        } catch (Exception ex) {
            System.err.println("Unable to capture screenshot: " + 
            ex.getCause());

}
    }
}

Again, the changes are minimal. We now extend RunListener, which is part of JUnit, instead of TestListenerAdaptor provided by TestNG. The name of the class we need to override has changed and it passes in a different variable. This means that we need to slightly change the code that gets the name of the failing test.

The only thing that is left to do is to modify the imports on our tests; you use the JUnit @Test annotation instead of the TestNG @Test annotation. To do this, we need to find all instances of the following:

import org.testng.annotations.Test;

We replace them with the following:

import org.junit.Test;

You are now ready to try running your tests again; let's perform the following in the Terminal to check that everything still works:

mvn clean verify

Let's check that threading still works as well:

mvn clean verify -Dthreads=2

Excellent. Everything seems to be working correctly; we now have a working JUnit implementation.

I did, however, mention that there would be some caveats, and unfortunately they are not instantly obvious. JUnit does not have a concept of @BeforeSuite like TestNG, so we have used the @BeforeClass annotation instead. When you have a single test class, it would appear to work in the same way as @BeforeSuite, however, that's not true. When we were using before suite, we could configure our thread pool before any tests were run and then clean it up after all tests were run. With the JUnit implementation, we configure our thread pool before each class is run and then clean it up after each class is run. It's a small difference, but it does result in more browser startup/shutdown time.

The easiest way to show this is by adding another test class. Copy the existing one and give it a slightly different name. Once you have done that, tweak the tests to search with a slightly different criteria. Now try running the following code again:

mvn clean verify -Dthreads=2

You will notice that, this time, two browser windows opened, the tests in the first class were run, and then the browser windows shut again. They then opened up again and ran the tests for the second class. You can add more tests to each of your test classes to reassure yourself that the browsers are being reused inside the class.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime
Visually different images