Hi all, in the previous article I explained and showed two common techniques to run your selenium tests in parallel with Selenium Grid and JUnit. In this post, I will show you to run your tests in parallel with another popular testing framework, TestNG.
Prerequisites
Nice to have: Before starting to read this post it is better to read previous selenium tutorials.
For Mac users, please check this article for Selenium Grid usage: https:/selenium-grid-on-mac/
For Windows users, please check this article for Selenium Grid usage: https:/selenium-grid/
Test Scenario:
We have two test classes. The first one has three test methods that open www.google.com and check that title is “Google”. The second one has two test methods and they open Google and Yandex and then check their titles. Tests are very simple but our aim is to run them in parallel with different browsers. We will run the first test with Chrome and the second one with Firefox. We will configure this within TestNG.xml file.
Making Your TestNG Tests Thread-Safe:
It is very important to construct our tests thread-safe in order to run them in parallel without a problem. We have to make sure that shared resources are isolated within each thread. Thus, we need to initialize all related resources within the test method. Also, we need to keep test-specific resources thread-local and keep your static class members as static that is really needed to be static. Apply these to all the classes that are loading during the test execution.
Test Architecture:
I used three JAVA files for our test. These are FirstTest.java, SecondTest.java, and BaseTest.java. Also, I did the configurations in TestNG.xml file.
- In BaseTest class, I created ThreadLocal <>() webdriver (ThreadLocalMap) for thread-safe test execution and I got the TestNG parameter (browser) with @Parameter annotation.
- I created and configured Browser Capabilities by using OptionsManager class and set our local grid address in TestBase class.
- CapabilityFactory returns browser Capabilities based on browser name.
- In BaseTest class, getDriver() method returns the created driver.
- FirstTest and SecondTest classes extend TestBase class and comprise of their test code.
Test Code:
Before run the test, you need to trigger Selenium Grid!
It is described at first section of this article. For Mac users please check here.
OptionsManager.java
I will start with Browser Options. You can set your options based on your requirements or you can use empty options. It is up to you.
import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxOptions; import org.openqa.selenium.firefox.FirefoxProfile; public class OptionsManager { //Get Chrome Options public static ChromeOptions getChromeOptions() { ChromeOptions options = new ChromeOptions(); options.addArguments("--start-maximized"); options.addArguments("--ignore-certificate-errors"); options.addArguments("--disable-popup-blocking"); //options.addArguments("--incognito"); return options; } //Get Firefox Options public static FirefoxOptions getFirefoxOptions () { FirefoxOptions options = new FirefoxOptions(); FirefoxProfile profile = new FirefoxProfile(); //Accept Untrusted Certificates profile.setAcceptUntrustedCertificates(true); profile.setAssumeUntrustedCertificateIssuer(false); //Use No Proxy Settings profile.setPreference("network.proxy.type", 0); //Set Firefox profile to capabilities options.setCapability(FirefoxDriver.PROFILE, profile); return options; } }
CapabilityFactory.java
We can get browser capabilities from an instance of this class. We can get the browser name from TestNG.xml file.
import org.openqa.selenium.Capabilities; public class CapabilityFactory { public Capabilities capabilities; public Capabilities getCapabilities (String browser) { if (browser.equals("firefox")) capabilities = OptionsManager.getFirefoxOptions(); else capabilities = OptionsManager.getChromeOptions(); return capabilities; } }
BaseTest.java
We can use CapabilityFactory to get browser options and we will use ThreadLocal for ThreadSafe execution.
import java.net.MalformedURLException; import java.net.URL; import org.openqa.selenium.WebDriver; import org.openqa.selenium.remote.RemoteWebDriver; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Parameters; public class BaseTest { //Declare ThreadLocal Driver (ThreadLocalMap) for ThreadSafe Tests protected static ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<>(); public CapabilityFactory capabilityFactory = new CapabilityFactory(); @BeforeMethod @Parameters(value={"browser"}) public void setup (String browser) throws MalformedURLException { //Set Browser to ThreadLocalMap driver.set(new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capabilityFactory.getCapabilities(browser))); } public WebDriver getDriver() { //Get driver from ThreadLocalMap return driver.get(); } @AfterMethod public void tearDown() { getDriver().quit(); } @AfterClass void terminate () { //Remove the ThreadLocalMap element driver.remove(); } }
FirstTest.java
import org.testng.Assert; import org.testng.annotations.Test; public class FirstTest extends TestBase { @Test public void GOOGLE1() { System.out.println("Google1 Test Started! " + "Thread Id: " + Thread.currentThread().getId()); getDriver().navigate().to("http://www.google.com"); System.out.println("Google1 Test's Page title is: " + getDriver().getTitle() +" " + "Thread Id: " + Thread.currentThread().getId()); Assert.assertEquals(getDriver().getTitle(), "Google"); System.out.println("Google1 Test Ended! " + "Thread Id: " + Thread.currentThread().getId()); } @Test public void GOOGLE2() { System.out.println("Google2 Test Started! " + "Thread Id: " + Thread.currentThread().getId()); getDriver().navigate().to("http://www.google.com"); System.out.println("Google2 Test's Page title is: " + getDriver().getTitle() +" " + "Thread Id: " + Thread.currentThread().getId()); Assert.assertEquals(getDriver().getTitle(), "Google"); System.out.println("Google2 Test Ended! " + "Thread Id: " + Thread.currentThread().getId()); } @Test public void GOOGLE3() { System.out.println("Google3 Test Started! " + "Thread Id: " + Thread.currentThread().getId()); getDriver().navigate().to("http://www.google.com"); System.out.println("Google3 Test's Page title is: " + getDriver().getTitle() +" " + "Thread Id: " + Thread.currentThread().getId()); Assert.assertEquals(getDriver().getTitle(), "Google"); System.out.println("Google3 Test Ended! " + "Thread Id: " + Thread.currentThread().getId()); } }
SecondTest.java
import org.testng.Assert; import org.testng.annotations.Test; public class SecondTest extends TestBase{ @Test public void GOOGLE4() { System.out.println("Google4 Test Started! " + "Thread Id: " + Thread.currentThread().getId()); getDriver().navigate().to("http://www.google.com"); System.out.println("Google4 Test's Page title is: " + getDriver().getTitle() +" " + "Thread Id: "+ Thread.currentThread().getId()); Assert.assertEquals(getDriver().getTitle(), "Google"); System.out.println("Google4 Test Ended! " + "Thread Id: " + Thread.currentThread().getId()); } @Test public void YANDEX() { System.out.println("Yandex Test Started! " + "Thread Id: " + Thread.currentThread().getId()); getDriver().navigate().to("http://www.yandex.com"); System.out.println("Yandex Test's Page title is: " + getDriver().getTitle() +" " + "Thread Id: " + Thread.currentThread().getId()); Assert.assertEquals(getDriver().getTitle(), "Yandex"); System.out.println("Yandex Test Ended! " + "Thread Id: " + Thread.currentThread().getId()); } }
TestNG.xml
You should create TestNG.xml under the top level of your project directory. If you are using IntelliJ IDEA, just right click your project name and then create a file and name is TestNG.xml and then copy and paste the below code into that file.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite thread-count="2" name="Suite" parallel="tests" > <test name="com.FirstTest" parallel="methods" thread-count="5"> <parameter name="browser" value="chrome"/> <classes> <class name="FirstTest"> <methods> <include name="GOOGLE1" /> <include name="GOOGLE2" /> <include name="GOOGLE3" /> </methods> </class> </classes> </test> <!-- First Test --> <test name="com.SecondTest" parallel="methods" thread-count="5"> <parameter name="browser" value="firefox"/> <classes> <class name="SecondTest"> <methods> <include name="GOOGLE4" /> <include name="YANDEX" /> </methods> </class> </classes> </test> <!-- Second Test --> </suite> <!-- Suite -->
pom.xml
<?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"> <modelVersion>4.0.0</modelVersion> <groupId>TestNGParallel</groupId> <artifactId>TestNGParallel</artifactId> <version>1.0-SNAPSHOT</version> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>3.141.59</version> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>7.1.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>test</scope> </dependency> </dependencies> </project>
Test Results:
Test GitHub Project
Github: https://github.com/swtestacademy/TestNGParallel
References
http://stackoverflow.com/questions/30353996/selenium-and-parallelized-junit-webdriver-instances
https://wiki.saucelabs.com/display/DOCS/Parallel+Testing+in+Java+with+Maven+and+TestNG
https://www.browserstack.com/automate/java#speed-up-testing
http://blog.wedoqa.com/2013/07/how-to-run-parallel-tests-with-selenium-webdriver-and-testng-2/
https://rationaleemotions.wordpress.com/2013/07/31/parallel-webdriver-executions-using-testng/
Selenium Webdriver Tutorial Series
[fusion_widget_area name=”avada-custom-sidebar-seleniumwidget” title_size=”” title_color=”” background_color=”” padding_top=”” padding_right=”” padding_bottom=”” padding_left=”” hide_on_mobile=”small-visibility,medium-visibility,large-visibility” class=”” id=””][/fusion_widget_area]

Onur Baskirt is a Software Engineering Leader with international experience in world-class companies. Now, he is a Software Engineering Lead at Emirates Airlines in Dubai.
How do you implement parallel testing with page factory. I tried this with page factory and its throwing thread safety issues.
Can you write a post for parallel testing when page factory is used
What kind of errors you faced with? Would you share your error messages or details of your problems?
hi Sir,
Dont we have to set system.set property in base class?from where does it take browser exe files?
Hi Pooja, You don’t need to do it. Open a directory on C:\ driver as “drivers”. Download and put chrome and firefox driver executables in that folder. After that, open environmental variables on windows. Add “C:\drivers” location into path variable. Then restart the PC. After that, you do not need to declare system.property line for driver executables in your automation codes.
For parallel tests, you need to do it in grid configuration also. You can find details in this article. https:/parallel-tests-selenium-grid-junit/
https://github.com/bonigarcia/webdrivermanager
use above project. will make life much easier
Setup as @beforeMethod and teardown as @AfterClass? Why not both @AfterMethod or @AfterClass?
You are right. It is better to change @AfterClass to @AfterMethod. I will also change the code a little bit soon. Thanks for your comment.
Hello, I update the code. Now, it runs flawlessly. Please check the new codes. :) I used Latest firefox and chrome drivers and Selenium 3.7.1
Hi. I have couple of questions about parallel running.
1- If I run test locally, Is it a good practice to use Selenium Grid?
2- Is it a common practice to call getDriver on each method when you run tests parallel? (Currently I call getDriver on class level and and methods in classes are dependant with each other, dependsOnMethods = {“ABC”},alwaysRun = true)
Thanks a lot! I learn a lot from your website.
1- Yes, you can use Selenium Grid for parallel test execution but also you can use Docker-Selenium, Zalenium, and Selenoid (Yandex) too.
2- If you run your test in parallel, it better not to use dependent methods. Each test (method)should be atomic and has a single responsibility. In this way, you can assure parallelism very well.
I appreciate your feedback. Thank you! :)
Thanks for this post Onur.
Can you please explain –
1. your one test i.e. @Test is equivalent to 1 test case ?
Example : I am working on Salesforce application and there are 3 test cases
1. TC1 – Login into Salesforce .
2. TC2 – Click on Agencies and create task
3. TC3 – Click on Lead and create task.
So will structure be like –
@Test
LoginintoApp()
@Test
ClickOnAgency
@Test
CreateTaskForAgency
In Such case My Login into app method will run in each @test which will make unnecessary login step execution thrice.
Could you please explain your approach how you would segregate above test cases in different class file and @test
For parallel execution, if a test case needs login, we need to login first. But if you run them in sequential order, then you can use TestNG priority or dependson features. A test case needs login and then do sth. you need to write the login case in it first, then do the required actions. I understand it is an unnecessary login step execution time but if you run for example 50 tests or more in parallel, it will not be a big problem. For this, I suggest you use Docker-Selenium or Selenoid solutions. Also, you need to write your tests with Page Object Model. So, you can write login actions only once in page class and use it in test classes.
Thanks for the post Onur,
Please let me know if there is way to run the TestNg tests in parallel, based on the number of nodes associated in a selenium grid hub. Instead of explicitly specifying the thread count, it should dynamically adjust the parallel run
Ex: if the nodes are 6 and 6 test cases, each test case should in one node. And if the nodes are 3 and test cases are 6, it should run 3 test cases first and then start the other 3 instead of starting all 6 at the same time
Hi Saravanan, Maybe you will do it in this way. You need to write a shell script to trigger selenium grid with for example 5 nodes. After that, you need to write a second script, to read the testng.xml file, modify thread-count as 5, then save it. After these operations, you can start your test automation project with maven command with modified testng.xml file. I hope you will get my strategy. You can modify the testng.xml file and trigger the grid by using .bat files, Python, Perl, Java, PowerShell etc. it is up to you. ;)
Thanks Onur
Hi Onur. Thanks for posting this. I can tell you definitely understand the issues with selenium grid, parallel execution, and having it be thread safe. Currently, my test were not written with thread safety in mind and they are able to execute fine when I run just one instance from Jenkins. If I try to run it in parallel using selenium grid, they fail to finish executing. It looks like it will be too much overhead for me to convert my entire test suite to be thread safe given my timeline. Is there any other recommendation to have my test run “in parallel” without making it thread safe? The only other ideas I have is to have multiple instances of Jenkins. I know it’s not the best solution, but I am looking for a workaround to have my sets of test run in parallel. Worst comes to worst, I will setup multiple instances of Jenkins on different machines. Thank you!
Hi Steve, one of the other solutions is Selenoid. We also tried this and will write an Article with Kaan Sariveli (our new automation engineer). If you have a time, you can also try Selenoid. It is not too hard to use. But thread-safety will be again needed. On the other hand, multiple Jenkins instances on different machines is a workaround solution but it comprises of manual effort too. At the same time, you need to take care of data while you are doing parallel execution and your tests should be as atomic as possible. Because in our case many tests have a scenario with login and we provide different login information for each test. As I understand, you need a very urgent solution. I suggest you do the way that you know such as multiple Jenkins, then you can enhance this solution in the mid or long run.
How did you create the ThreadLocal class?
All code is here: https://github.com/swtestacademy/TestNGParallel
I will also re-check and will update the code if required, Then, let u know again. Thanks.
The repo was a bit messy. I will try to create a new repo and update the project. Thank you for feedback.
I updated the code. Now, it is cleaner. Please check the master branch.
Hi, does that work when changing the second test to . do the 2 browsers open?
Sorry. I means parameter name=”browser” value=”firefox”
yes like that
Hi, for more info, I don’t use the @Parameters browser in @tests, I customize the “browser” variable to run test through Jenkins. I tried to change the second test to parameter name=”browser” value=”firefox” and run but the 2 instance of chrome or firefox start, not both. Do you have any solution on this? Thanks
I need to check that at home but I can check without Jenkins. I will update u whenever I find a chance to look at it.
Hi Jame,
Please check this article: https:/jenkins-selenium-java-maven/
Here we parametrized the environment rather than the environment, you need to parametrize the browser and in the base test class, set the browser. In this case, u should remove browser parameter from testng xml and u don’t need to use @Parameter annotation at BaseTest. I hope this helps. I also updated and simplified the project.
Yes it worked like that.
This is good, but how to start? And the project modell?
Thanks for your answer. :)
https://github.com/swtestacademy/TestNGParallel