Parallel Testing without Selenium Grid (On your Local PC)

In this article, I will describe how to do parallel testing on your PC (local computer) without using Selenium Grid. There are several techniques to do this operation and I will share with you some code samples.

In my former articles, I described parallel testing theory so in this tutorial I do not want to go into details. I will give the test scenarios and their implementations. Also, I will describe the details of each test files and I will add informative comments in my test automation codes.

Test Scenario for Parallel Testing

  • We will have two test classes.
  • First Class has three methods and all methods must open Google and check its title. First class’s test methods must run on Firefox.
  • Second Class has two methods and first test method must open Google and checks its title, second test method opens Yandex and checks its title. Second class’s test methods must run on Chrome.
  • All tests have to Run in Parallel in Locally!

Test Design:

1) OptionsManager Class:

This class has a “getChromeOptions” and “getFifefoxOptions” methods and they take browser name as a parameter then they provide related options as per specific browser types.

package com;

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;
        /*ChromeDriverService service = new ChromeDriverService.Builder()
                .usingAnyFreePort()
                .build();
        ChromeDriver driver = new ChromeDriver(service, 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;
    }
}

2) ThreadLocal Driver Factory Class:

First of all, for Thread-Safe test automation, I will use ThreadLocal<WebDriver> and I will create a ThreadLocalDriverFactory (TLDriverFactory) class to set ThreadLocal driver and get Webdriver.

package com;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.WebDriverWait;

/**
 * Created by onurb on 19-Dec-16.
 */
public class TLDriverFactory {

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

    public synchronized static void setTLDriver (String browser) {
        if (browser.equals("firefox")) {
            tlDriver = ThreadLocal.withInitial(() -> new FirefoxDriver(OptionsManager.getFirefoxOptions()));
        } else if (browser.equals("chrome")) {
            tlDriver = ThreadLocal.withInitial(() -> new ChromeDriver(OptionsManager.getChromeOptions()));
        }
    }

    public synchronized static WebDriver getTLDriver () {
        return tlDriver.get();
    }
}

3) TestBase Class:

In Base Test class,  @BeforeMethod I got browser parameter from TestNG.xml file (I will describe it soon),  and I set and get ThreadLocal Driver by using TLDriverFactory class. @AfterMethod, I closed the driver.

package com;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.*;

import java.net.MalformedURLException;

/**
 * Created by ONUR on 03.12.2016.
 */
public class TestBase {

    protected WebDriverWait wait;

    //Do the test setup
    @BeforeMethod
    @Parameters(value={"browser"})
    public void setupTest (@Optional String browser) throws MalformedURLException {
        //Set & Get ThreadLocal Driver with Browser
        TLDriverFactory.setTLDriver(browser);
        wait = new WebDriverWait(TLDriverFactory.getTLDriver(), 15);
    }

    @AfterMethod
    public synchronized void tearDown() throws Exception {
        TLDriverFactory.getTLDriver().quit();
    }

}

4) FirstTest Class:

package com;

import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * Created by ONUR on 03.12.2016.
 */
public class FirstTest extends TestBase {

    @Test
    public void GOOGLE0() throws Exception {
        System.out.println("Google0 Test Started! " + Thread.currentThread().getId());
        TLDriverFactory.getTLDriver().navigate().to("http://www.google.com");
        System.out.println("Google0 Test's Page title is: " + TLDriverFactory.getTLDriver().getTitle() + " " + Thread.currentThread().getId());
        Assert.assertEquals(TLDriverFactory.getTLDriver().getTitle(), "Google");
        System.out.println("Google0 Test Ended! " + Thread.currentThread().getId());
    }

    @Test
    public void GOOGLE2() throws Exception {
        System.out.println("Google2 Test Started! " + Thread.currentThread().getId());
        TLDriverFactory.getTLDriver().navigate().to("http://www.google.com");
        System.out.println("Google2 Test's Page title is: " + TLDriverFactory.getTLDriver().getTitle() + " " + Thread.currentThread().getId());
        Assert.assertEquals(TLDriverFactory.getTLDriver().getTitle(), "Google");
        System.out.println("Google2 Test Ended! " + Thread.currentThread().getId());
    }

    @Test
    public void GOOGLE3() throws Exception {
        System.out.println("Google3 Test Started! " + Thread.currentThread().getId());
        TLDriverFactory.getTLDriver().navigate().to("http://www.google.com");
        System.out.println("Google3 Test's Page title is: " + TLDriverFactory.getTLDriver().getTitle() + " " + Thread.currentThread().getId());
        Assert.assertEquals(TLDriverFactory.getTLDriver().getTitle(), "Google");
        System.out.println("Google3 Test Ended! " + Thread.currentThread().getId());
    }

    @Test
    public void GOOGLE4() throws Exception {
        System.out.println("Google4 Test Started! " + Thread.currentThread().getId());
        TLDriverFactory.getTLDriver().navigate().to("http://www.google.com");
        System.out.println("Google3 Test's Page title is: " + TLDriverFactory.getTLDriver().getTitle() + " " + Thread.currentThread().getId());
        Assert.assertEquals(TLDriverFactory.getTLDriver().getTitle(), "Google");
        System.out.println("Google3 Test Ended! " + Thread.currentThread().getId());
    }

}

5) SecondTest Class:

package com;

import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * Created by ONUR on 03.12.2016.
 */
public class SecondTest extends TestBase{

    @Test
    public void GOOGLE1() throws Exception {
        System.out.println("Google1 Test Started! " + Thread.currentThread().getId());
        TLDriverFactory.getTLDriver().navigate().to("http://www.google.com");
        System.out.println("Google1 Test's Page title is: " + TLDriverFactory.getTLDriver().getTitle() + " " + Thread.currentThread().getId());
        Assert.assertEquals(TLDriverFactory.getTLDriver().getTitle(), "Google");
        System.out.println("Google1 Test Ended! " + Thread.currentThread().getId());
    }

    @Test
    public void YANDEX() throws Exception {
        System.out.println("Yandex Test Started! " + Thread.currentThread().getId());
        TLDriverFactory.getTLDriver().navigate().to("http://www.yandex.com");
        System.out.println("Yandex Test's Page title is: " + TLDriverFactory.getTLDriver().getTitle() + " " + Thread.currentThread().getId());
        Assert.assertEquals(TLDriverFactory.getTLDriver().getTitle(), "Yandex");
        System.out.println("Yandex Test Ended! " + Thread.currentThread().getId());
    }
}

6) TestNG.xml 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="firefox"/>
        <classes>
            <class name="com.FirstTest">
                <methods>
                    <include name="GOOGLE0" />
                    <include name="GOOGLE2" />
                </methods>
            </class>
        </classes>
    </test> <!-- First Test -->
    <test name="com.SecondTest"  parallel="methods" thread-count="4">
        <parameter name="browser" value="chrome"/>
        <classes>
            <class name="com.SecondTest">
                <methods>
                    <include name="GOOGLE1" />
                </methods>
            </class>
        </classes>
    </test> <!-- Second Test -->
</suite> <!-- Suite -->

7) 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>RELEASE</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>RELEASE</version>
        </dependency>
    </dependencies>
</project>

Click here to go to Github Link

LATEST NOTES

If you have several test classes and in those test classes if you have more than one test methods, then you can run those test classes and methods in parallel with below TestNG configuration.

<?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"/>
        <parameter name="platform" value="WINDOWS"/>
        <classes>
            <class name="com.FirstTest">
                <methods>
                    <include name="GOOGLE0" />
                    <include name="GOOGLE2" />
                </methods>
            </class>
        </classes>
    </test> <!-- First Test -->
    <test name="com.SecondTest"  parallel="methods" thread-count="4">
        <parameter name="browser" value="chrome"/>
        <parameter name="platform" value="WINDOWS"/>
        <classes>
            <class name="com.SecondTest">
                <methods>
                    <include name="GOOGLE1" />
                </methods>
            </class>
        </classes>
    </test> <!-- Second Test -->
</suite> <!-- Suite -->

Above configuration runs two parallel test classes because of below line.

<suite thread-count=”2″ name=”Suite” parallel=”tests” >

And it also runs five test methods of test class one in parallel too by using below line.

<test name=”com.FirstTest” parallel=”methods” thread-count=”5″>

And it also runs four test methods of test class two in parallel too by using below line.

<test name=”com.SecondTest” parallel=”methods” thread-count=”4″>

Here is the result for parallel testing in local machine!

Execution Order of Parallel Testing in Local Computer:

Google2 Test Started! 15
Google1 Test Started! 13
Google0 Test Started! 14
Google1 Test’s Page title is: Google 13
Google1 Test Ended! 13
Google2 Test’s Page title is: Google 15
Google0 Test’s Page title is: Google 14
Google2 Test Ended! 15
Google0 Test Ended! 14

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]

Thanks.
-Onur

21 thoughts on “Parallel Testing without Selenium Grid (On your Local PC)”

  1. Hi Onur – Wonderful Article and great information ..

    Followed the steps and implemented ,working perfectly.

    I have doubt here , actually here we are opening 2 Threads 1 for chrome and another for Firefox and executing all the testcases from respective suites.

    But if i wanted to execute testcases of same suite in different threads (even if its for the same browsers)

    for eg: Test Suite – 1 having testcase-1 , testcase-2 , testcase-3 , testcase-4 , testcase-5 – (Note : Each test case is individual component , every need to open & close browser****)

    In TestNG.XML – Will pass as Test Suite – 1 execution with Chrome & Test Suite – 1 execution with Firefox & execute .

    Can you please throw light on the above ?

    Cheers , Chaithanya.

    Reply
  2. Thanks for your very quick turn around …

    Gone thru the XML file in the email , Actually even there it will be opening in 4 different thread (2 for chrome & 2 for Firefox) and all the test cases in test suite will be executed in the same threads.

    But i am looking like

    Then all the 3 test cases in FirstTest suite should run in 3 different threads parallel as my test cases are independent where 3 chrome browsers will open and perform respective actions as per the test case.

    Please guide me in this.

    Thanks, Chaithanya

    Reply
  3. Woooooww .. You are Wonderful …! Great efforts Onur…Really much more appreciated all your time and support .. will implement and get back you .. again Thanks a lot ..

    Cheers, Chaithanya.

    Reply
  4. Really now a days , I wont see a person like you “ONUR” .

    In spite of your busy schedules , your turn around time and solution definitions are fantastic.

    I visited many blogs and posted many questions , but you are different.

    Actually i know i bugged you a lot ,but still you are so patient to answer them ( Best part never seen or know whom i am )

    ” ONUR – YOU ARE AWESOME”. and wish some day will catch you. By the way we implemented this and working perfectly.

    Cheers, Chaithanya

    Reply
  5. Great article!
    I am also curious about how to use test user data on parallel testing. (e.g If I need to login each time and do some actions) Should I use new user data on each run or on each test method ? (In case the tests will effect on each other?)
    I see on sauce labs best practices “We should create a new data whenever your tests aren’t interacting with previous or future tests”
    Thanks a lot Mr Onur. Have a good day

    Reply
    • Xian, you are great! This is a great question and a good challenge. One of the thing that I am planning to implement for our test automation project is to set an available status for each data. For example, if I have a dataset in excel or DB, when a test method is starting to use that data, it should set to No or 0 to make it not available for usage. When that code’s job is finished, it should release that data and set 1 or Yes for its availability. Also, each test should check the data availability before the test execution. In some cases, it is much better to create a data and use it in the test and remove it in the teardown of the test. Also, you can write a service that creates user data, removes user data. It will be an API for your tests.

      Reply
  6. Hi Onur,

    I have been trying to run parallel testing on my local machine which is on mac, but all in vain. Sm currently using JUnit will this setup work for me at all, if not can you advise me on the best way to do it. My email address is [email protected]

    Thanks

    Reply
  7. Onur, This is a great article on parallel testing. This article gave a good learning for me. Thanks for sharing this.

    Keep writing :)

    Reply
    • Thank you Ganesh. I will try to write more articles. Also, I am planning to update articles as well. If you faced with any problems please let me know and I will check and update the article. Happy automating.

      Reply
  8. Thanks so much for this article.
    “In my former articles, I described parallel testing theory”
    Could you please provide the link to those articles

    Reply
  9. Hi Onur

    I have a query .
    Suppose there are 10 classes within a package .
    I want to run the first 6 in parallel and the rest 4 sequentially .Please email me the testng.xml for this scenario.

    Thanks

    Reply
  10. Hi Onur,

    Thanks for the article. I wonder that how we can implement this parallel test concept to Page Factory or Page Object model.
    So let’s imagine that it is a more complex project, for example :

    – Page Classes (LoginPage.java,HomePage.java),

    – Test Classes (LoginTest.java,HomeTestJava),

    – TestBase class

    – PageHelper class (like common methods click,sendKeys,waiting vs..)

    with TestNG.xml we can run like this project, how we can manage ThreadLocal ? I would appreciate it if you could share an article about this topic.

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.