Parallel Testing With the TestNG Java Test Framework

Karishma Nimavat, Jessica Cyrus, Nex, https://www.nexsoftsys.com/

Introduction

Parallel execution is a concept that has a huge importance in the world of automation testing. Any kind of work that we do in parallel always saves time. Similarly, in end-to-end testing of an application, running tests in parallel instead of running sequentially saves our execution time which can further save our time in the remaining phases of the software testing life cycle and application delivery.

Executing our tests in parallel supercharge our Selenium testing by running maximum test cases in minimum time. It can help large organizations in conducting large scale testing. However, there are a few points that automation testers should keep in mind before running their tests in parallel such as avoiding one test dependency on another test and setting up the test parameters so that the failed tests get easily reproducible from the multiple executed tests.

Running tests in parallel can have different scenarios depending on the testing requirements:

Execution of different scripts at a time on the same configuration.

Execution of the same script at a time on different configurations.

Here the word "configuration" refers to the testing environment which may include different browsers, browser versions, operating system and resolution.

Application compatibility issues have always been a nightmare for developers. Parallel testing plays a very important role when testing enters the phase of cross-browser testing.

Scenario based Example

Let us consider the first scenario with the Java TestNG framework which is "executing different scripts at a time on the same configuration". Here, we would be creating two different tests scripts and one test suite. Our test suite would be in XML format in which our tests scripts classes would be defined and this TestNG XML file would act as a test runner.

First Test Script: AppleTest.java

package MyPackage; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; public class AppleTest { public WebDriver driver; @BeforeTest public void setUp() throws InterruptedException, IOException { System.out.println("Setting up Chrome Driver before Apple test"); System.setProperty("webdriver.chrome.driver", "C:\\Users\\Lenovo-I7\\Desktop\\Selenium\\chromedriver.exe"); driver = new ChromeDriver(); } @Test public void apple_test() throws InterruptedException { try { driver.get("https://www.apple.com/"); driver.manage().window().maximize(); driver.findElement (By.xpath("//*[@id=\'ac-globalnav\']/div/ul[2]/li[3]")).click(); driver.manage().timeouts().implicitlyWait(5,TimeUnit.SECONDS) ; driver.findElement( By.cssSelector("#chapternav > div > ul > li.chapternav-item.chapternav-item-ipad-air > a")).click(); driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS) ; driver.findElement(By.linkText("Why iPad")).click(); driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS) ; driver.findElement(By.id("ac-gn-link-search")).click(); driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS) ; WebElement airpods = driver.findElement(By.id("ac-gn-searchform-input")); airpods.sendKeys("airpods"); driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS) ; driver.findElement (By.xpath("//*[@id=\"quicklinks\"]/li[1]/a")).click(); Thread.sleep(3000); } catch(Exception e) { System.out.println(e.getStackTrace()); } } @AfterTest public void tearDown() { System.out.println("Test Execution completed for Apple Test" + "

"); driver.quit(); } }

Second Test Script: RaymondWeilTest.java

package MyPackage; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; public class RaymondWeilTest { public WebDriver driver; @BeforeTest public void setUp() throws InterruptedException, IOException { System.out.println ("Setting up Chrome Driver before Raymond Weil Test"); System.setProperty("webdriver.chrome.driver", "C:\\Users\\Lenovo-I7\\Desktop\\Selenium\\chromedriver.exe"); driver = new ChromeDriver(); } @Test public void apple_test() throws InterruptedException { try { driver.get("https://www.raymond-weil.com"); driver.manage().window().maximize(); JavascriptExecutor js = (JavascriptExecutor)driver; js.executeScript("window.scrollBy(0,1000)"); Thread.sleep(2000); driver.findElement (By.xpath("//*[@id=\"et-boc\"]/div/div[5]/div/div[4]/div[3]/a")).click(); driver.manage().timeouts().implicitlyWait(5,TimeUnit.SECONDS); js.executeScript("window.scrollBy(0,1600)"); Thread.sleep(2000); String watchName = driver.findElement By.xpath("//*[@id=\"left-area\"]/ul/li[9]/a[1]/h2")).getText(); System.out.println(watchName + "

"); Thread.sleep(3000); } catch(Exception e) { System.out.println(e.getStackTrace()); } } @AfterTest public void tearDown() { System.out.println ("Test Execution completed for Raymond Weil Test" + "

"); driver.quit(); } }

Test Runner: TestNG.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite thread-count="2" verbose="1" name="Blog-Suite" parallel="tests"> <test name="Apple"> <classes> <class name="MyPackage.AppleTest"/> </classes> </test> <test name="Raymond Weil"> <classes> <class name="MyPackage.RaymondWeilTest"/> </classes> </test> </suite>

Code Walkthrough:

In both our test scripts i.e. AppleTest and RayondWeilTest, we have been using TestNG annotations to provide instructions to the compiler while running the tests.

In BeforeTest annotation, we have set up our test environment before running the test. Once we have set up the test environment, we have written our test case in Test annotation method. After the test method, we have defined AfterTest annotation which ends or teardown the session by closing the browser. No matter your test case pass or fail, the AfterTest annotation method would always be executed after your test case completion.

Coming to the test runner XML file, the first step is to create suite which defines few attributes:

Thread-count: Defines the number of threads that would be running in parallel.

Defines the number of threads that would be running in parallel. Verbose : Provide the logs of current execution. It accepts value 1-10. You can increase the value to get additional and clear logs.

: Provide the logs of current execution. It accepts value 1-10. You can increase the value to get additional and clear logs. name : Defines your test suite name

: Defines your test suite name Parallel: Defines what to run in parallel. It accepts values such as tests, classes, methods and suites.

Once the suite is defined with the above attributes, we have defined our test script by providing a test tag that further includes our class name that is to be executed. The class name syntax should be PackageName.ClassName.

Since we have two different test scripts to be run in parallel, we would define each test script in different test tags according to the test name.

Right-click on the TestNG XML file and select Run As-> TestNG Suite to get the below console output:

(click on figure to enlarge)

Now, let us consider the second scenario here which is "executing the same script at a time on different configurations". In this case, we will only consider one test script i.e. AppleTest, having the same annotations as described before. Here we would be running the same script on two different browsers(Chrome and Firefox) for which we have used one more annotation called @Parameter that is used to parameterize the test script, syntax : @Parameters({ "param1" , "param2"}). The values of these parameters are defined in TestNG.xml file.

In the example below, the Parameter annotation has been used with the BeforeTest and AfterTest annotation method.

Test Script: AppleTest

package MyPackage; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.Reporter; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class AppTest { public WebDriver driver; @Parameters({ "browserName" }) @BeforeTest public void setUp(String browserName) throws InterruptedException, IOException { if (browserName.equalsIgnoreCase("firefox")) { System.out.println("Setting up Firefox Driver for Apple test"); System.setProperty("webdriver.gecko.driver", "C:\\Users\\Lenovo-I7\\Desktop\\Selenium\\geckodriver.exe"); driver = new FirefoxDriver(); } else if (browserName.equalsIgnoreCase("chrome")) { System.out.println("Setting up Chrome Driver for Apple test"); System.setProperty("webdriver.chrome.driver", "C:\\Users\\Lenovo-I7\\Desktop\\Selenium\\chromedriver.exe"); driver = new ChromeDriver(); } } @Test public void apple_test() throws InterruptedException { try { driver.get("https://www.apple.com/"); driver.manage().window().maximize(); driver.findElement (By.xpath("//*[@id=\'ac-globalnav\']/div/ul[2]/li[3]")).click(); driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS); driver.findElement( By.cssSelector("#chapternav > div > ul > li.chapternav-item.chapternav-item-ipad-air > a")).click(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.findElement(By.linkText("Why iPad")).click(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.findElement(By.id("ac-gn-link-search")).click(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); WebElement airpods = driver.findElement (By.id("ac-gn-searchform-input")); airpods.sendKeys("airpods"); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.findElement (By.xpath("//*[@id=\"quicklinks\"]/li[1]/a")).click(); Thread.sleep(3000); } catch (Exception e) { System.out.println(e.getStackTrace()); } } @Parameters({ "browserName" }) @AfterTest public void tearDown(String browserName) { System.out.println ("Test Execution completed for: " + browserName + "

"); driver.quit(); } }

Test Runner: TestNG.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite thread-count="2" verbose="1" name="Blog-Suite" parallel="tests"> <test name="Firefox Test"> <parameter name="browserName" value="firefox" /> <classes> <class name="MyPackage.AppTest" /> </classes> </test> <test name="Chrome Test"> <parameter name="browserName" value="chrome" /> <classes> <class name="MyPackage.AppTest" /> </classes> </test> </suite>

Code walkthrough:

In the test script, the Parameter annotation has been used with BeforeTest and AfterTest annotation method. The Parameter annotation is parameterized with the browserName whose value is defined in TestNG.xml with Chrome and Firefox.

The setup method of BeforeTest annotation will setup the Firefox driver when the test parameter value from XML will be Firefox as a browserName and similarly for chrome driver when the test parameter value from XML will be chrome as a browserName.

Once your test case is executed, you can refresh your project to get a TestNG default report named as "emailable-report.html" in the test-output folder of the same project.

The report provides a short summary of your test suite with the test logs as well as the number of tests passed and failed. The report includes the start time for each test which is in UNIX format, you can use epoch converter to get this time in human readable format.

(click on figure to enlarge)

Summarizing it all!!

We have probably covered all the scenarios that software testing experts may require to cover in the testing phase in terms of parallel testing. Parallel testing is feasibly preferred when there is a requirement for cross-browser testing which might require setting up the Selenium Grid or accessing any cloud-based platform providing you the Selenium Grid to run their automated tests either parallel or sequentially on different configurations. Parallel testing not only saves the tester time but also makes the deployment process much faster after the positive results of verifications and validations done in phase of testing. So, give it a try to supercharge your automation testing :)

Related Software Testing and Quality Assurance Resources

Click here to view the complete list of Methods & Tools articles

This article was originally published in February 2020