Parallel WebDriver executions using TestNG

In this post, we will see how does one make use of TestNG to kick off parallel UI tests using WebDriver.

So lets try doing this with a typical cooking recipe style :)

So here are the ingredients that are required.

  • A Factory class that will create WebDriver instances
  • A Manager class that can be accessed to retrieve a WebDriver instance
  • A TestNG listener that will be responsible for instantiating the WebDriver instance automatically

So without wasting any time lets see how this all blends in.

First lets look at our Factory class. This is a very simplified Factory class that will create instances of WebDriver based upon the browser flavour. I have purposefully kept it simple only for illustration purposes:
Here’s how the Factory class will look like:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

package organized.chaos;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.ie.InternetExplorerDriver;

class LocalDriverFactory {

    static WebDriver createInstance(String browserName) {

        WebDriver driver = null;

        if (browserName.toLowerCase().contains("firefox")) {

            driver = new FirefoxDriver();

            return driver;

        }

        if (browserName.toLowerCase().contains("internet")) {

            driver = new InternetExplorerDriver();

            return driver;

        }

        if (browserName.toLowerCase().contains("chrome")) {

            driver = new ChromeDriver();

            return driver;

        }

        return driver;

    }

}

As you can see its a very simple class with a static method that creates WebDriver instances. The one interesting part to be noted here is that the class has been purposefully given only package visibility [ notice how the keyword "public" is missing from the class declaration ]. One of the many aspects that are involved in designing APIs is “Hide what is not necessary to be visible to your user”. For you to be able to drive a car, you don’t need to know how the piston works or how the fuel injection happens do you :)

Now lets take a look at how our Manager class would look like. The Manager class essentially uses a concept in java called ThreadLocalvariables.

The code would look like below :


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

package organized.chaos;

import org.openqa.selenium.WebDriver;

public class LocalDriverManager {

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

    public static WebDriver getDriver() {

        return webDriver.get();

    }

    static void setWebDriver(WebDriver driver) {

        webDriver.set(driver);

    }

}

Were you surprised that its such a small class ? :)
So as you can see we basically have a static ThreadLocal variable wherein we are setting webDriver instances and also querying webdriver instances as well.

Next comes the TestNG listener. The role of the TestNG listener is to perform “Automatic webdriver instantiation” behind the scenes without your test code even realising it. For this we will make use of IInvokedMethodListener so that the WebDriver gets instantiated right before a Test Method gets invoked and the webDriver gets automatically quit right after the Test method.
You can improvize this by incorporating custom annotations as well and parsing for your custom annotations [ The current implementation that you will see basically spawns a browser irrespective of whether you want to use it or not. That‘s not a nice idea all the time is it ]


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

package organized.chaos;

import org.openqa.selenium.WebDriver;

import org.testng.IInvokedMethod;

import org.testng.IInvokedMethodListener;

import org.testng.ITestResult;

public class WebDriverListener implements IInvokedMethodListener {

    @Override

    public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {

        if (method.isTestMethod()) {

            String browserName = method.getTestMethod().getXmlTest().getLocalParameters().get("browserName");

            WebDriver driver = LocalDriverFactory.createInstance(browserName);

            LocalDriverManager.setWebDriver(driver);

        }

    }

    @Override

    public void afterInvocation(IInvokedMethod method, ITestResult testResult) {

        if (method.isTestMethod()) {

            WebDriver driver = LocalDriverManager.getDriver();

            if (driver != null) {

                driver.quit();

            }

        }

    }

}

Now that we have shown all of the ingredients, lets take a look at a sample test as well, which is going to use all of this.


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package organized.chaos;

import org.testng.annotations.Test;

public class ThreadLocalDemo {

    @Test

    public void testMethod1() {

        invokeBrowser("http://www.ndtv.com");

    }

    @Test

    public void testMethod2() {

        invokeBrowser("http://www.facebook.com");

    }

    private void invokeBrowser(String url) {

        System.out.println("Thread id = " + Thread.currentThread().getId());

        System.out.println("Hashcode of webDriver instance = " + LocalDriverManager.getDriver().hashCode());

        LocalDriverManager.getDriver().get(url);

    }

}

As you can see its a very simple test class with two test methods. Each of the test methods opens up a different website. I have also add print statements for printing the thread id [yes thats the only reliable way of figuring out if your test method is running in parallel or in sequential mode. If you see unique values for Thread.currentThread().getId() then you can rest assured that TestNG is invoking your test methods in parallel.
We are printing the hashCode() values for the browser to demonstrate the fact that there are unique and different webDriver instances being created for every test method. [ Remember hashCode() value for an object would always be unique ]

Now lets take a look at how our suite file looks like :


1

2

3

4

5

6

7

8

9

10

11

12

13

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">

<suite name="Suite" parallel="methods">

<listeners>

<listener class-name="organized.chaos.WebDriverListener"></listener>

</listeners>

    <test name="Test">

        <parameter name="browserName" value="firefox"></parameter>

        <classes>

            <class name="organized.chaos.ThreadLocalDemo" />

        </classes>

    </test> <!-- Test -->

</suite> <!-- Suite -->

So when you run this test this is how your output would look like [ apart from you seeing two firefox windows popup on your desktop ]


1

2

3

4

5

6

7

8

9

10

11

12

[TestNG] Running:

  /githome/PlayGround/testbed/src/test/resources/threadLocalDem.xml

Thread id = 10

Hashcode of webDriver instance = 1921042184

Thread id = 9

Hashcode of webDriver instance = 2017986718

===============================================

Suite

Total tests run: 2, Failures: 0, Skips: 0

===============================================

And thus we have managed to leverage TestNG and run WebDriver tests in parallel without having to worry about race conditions or leaving browsers open etc.,

Hope that clears out some of the confusions and helps you get started with WebDriver automation powered by TestNG.

时间: 2024-11-05 07:12:20

Parallel WebDriver executions using TestNG的相关文章

selenium WebDriver:使用TestNG和CSV文件进行数据驱动

1.如何解决CSV文件乱码问题 讲csv文件用UltraEdit-32软件打开,在底部状态栏有标识中将编码类型变为utf-8 2.具体结合csv数据驱动代码 csv文件 package cn.gloryroad; import org.testng.annotations.Test;import org.testng.annotations.BeforeMethod;import org.testng.annotations.AfterMethod;import org.testng.annot

selenium WebDriver:使用TestNG、POI和Excel文件进行数据驱动

package cn.gloryroad; import org.testng.annotations.Test;import org.testng.annotations.BeforeMethod;import org.testng.annotations.AfterMethod;import org.testng.annotations.DataProvider;import java.io.File;import java.io.BufferedReader;import java.io.

WebDriver - 添加失败截图

WebDriver失败截图可以通过两种方式实现: 1. Use WebdriverEventListener 第一步:创建自己的WebDriverEventListener 创建自己的WebDriverEventListener 重写Onexception 方法, 当webdriver 遇到异常的时候执行截图动作. import java.io.File; import java.io.IOException; import java.io.FileOutputStream; import ja

webdriver location

s页面代码: <html><body> <form id=”loginForm”> <input name=”username” type=”text” /> <input name=”password” type=”password” /> <input name=”continue” type=”submit” value=”login” /> <input name=”continue” type=”button” val

PatentTips - Heterogeneous Parallel Primitives Programming Model

BACKGROUND 1. Field of the Invention The present invention relates generally to a programming model for a heterogeneous processor system. 2. Background Art With the success of programming models such as OpenCL and CUDA, heterogeneous computing platfo

WebDriver - 失败截图

WebDriver - 添加失败截图 WebDriver - 添加失败截图 作者: Max.Bai 时间: 2015/01 WebDriver失败截图可以通过两种方式实现: 1. Use WebdriverEventListener 第一步:创建自己的WebDriverEventListener 创建自己的WebDriverEventListener 重写Onexception 方法, 当webdriver 遇到异常的时候执行截图动作. import java.io.File; import j

项目管理及自动构建工具Maven

项目管理及自动构建工具Maven 一.Maven安装.目录结构.cmd命令1.下载安装apache-maven-3.2.3-bin.zip下载:http://maven.apache.org/download.cgi 安装:解压,配置环境变量M2_HOME=D:\Idea\config\apache-maven-3.2.3Path+=D:\Idea\config\apache-maven-3.2.3\bin 通过执行 mvn -v 可以查看当前版本号 C:\Users\yuki>mvn -v A

selenium测试框架篇,页面对象和元素对象的管理

前期已经做好使用Jenkins做buildhttp://www.cnblogs.com/tobecrazy/p/4529399.html 做自动化框架,不可避免的就是对象库. 有一个好的对象库,可以让整个测试体系: 更容易维护 大大增加代码重用 增加测试系统的稳定性 这里先了解一下我所说的对象库: 所谓的页面对象,是指每一个真是的页面是一个对象. 比如zhihu的登陆页面是一个页面对象,http://www.zhihu.com/#signin 这个页面对象主要包含一个输入邮箱的输入框(一个元素对

selenium测试框架篇

做自动化框架,不可避免的就是对象库. 有一个好的对象库,可以让整个测试体系: 更容易维护 大大增加代码重用 增加测试系统的稳定性 这里先了解一下我所说的对象库: 所谓的页面对象,是指每一个真是的页面是一个对象. 比如zhihu的登陆页面是一个页面对象,http://www.zhihu.com/#signin 这个页面对象主要包含一个输入邮箱的输入框(一个元素对象),一个输入密码的密码框 一个登陆框.当然,zhihu不止一个页面,有无数页面,每一个页面都可以封装为一个对象.而每个 页面的元素,也可