Skip to main content

Run Tests in Parallel

Run Java tests in parallel on BrowserStack to achieve faster build durations

On BrowserStack, you can run multiple Selenium Webdriver tests at the same time across various browser, device and OS combinations. This is called Parallel Testing. Parallel Testing gives you the same benefits as running a multi-threaded application.

With Parallel Testing, you can run the same test on different browser/device combinations i.e. cross-browser testing, or run different tests on the same or different browser/device combinations. Parallel Testing will help you reduce the run time of your test suite, resulting in faster build times and faster releases.

You can start testing in parallel using one of the popular test frameworks which work with Java and Selenium or refer the section for running tests in parallel without any framework in particular. We have provided getting started guides on some of the popular frameworks below:

Run tests in parallel without a framework

You can achieve parallel testing in multiple ways even if you are not using one of the popular test frameworks which has feature for enabling parallel testing. We show 3 such ways below but this is not an exhaustive list:

  1. Using a multi-threaded Java program (manually managing threads)
  2. Using a multi-threaded program and using an ExecutorService to manage threads
  3. Writing a shell script to invoke multiple Java programs in parallel

Parallel tests using a multi-threaded program

The following sample script shows a multi-threaded Java program. These are the salient points of the script:

  • The same script is run across 3 different browser/device combinations viz. iPhone 12 Pro, Samsung Galaxy S20, and Safari on Big Sur.
  • The capabilities are being populated in a HashTable and that is being passed on to the test function.
  • The test function has been written as a separate method and it takes care of the starting of the test for each of the browsers after iterating on the HashTable that is passed on to it.
  • Multi-threading has been implemented by defining multiple classes, all of which implements the Runnable Java class.
  • The main class contains the main() method and all the 3 threads are invoked from the main class.
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
class DeviceOne implements Runnable {
	public void run() {
		Hashtable<String, String> capsHashtable = new Hashtable<String, String>();
		capsHashtable.put("device", "iPhone 12 Pro");
		capsHashtable.put("real_mobile", "true");
		capsHashtable.put("build", "BStack-[Java] Sample Build");
		capsHashtable.put("name", "Thread 1");
		JavaSample deviceOne = new JavaSample();
		deviceOne.executeTest(capsHashtable);
	}
}
class DeviceTwo implements Runnable {
	public void run() {
		Hashtable<String, String> capsHashtable = new Hashtable<String, String>();
		capsHashtable.put("device", "Samsung Galaxy S20");
		capsHashtable.put("real_mobile", "true");
		capsHashtable.put("build", "BStack-[Java] Sample Build");
		capsHashtable.put("name", "Thread 2");
		JavaSample deviceTwo = new JavaSample();
		deviceTwo.executeTest(capsHashtable);
	}
}
class DeviceThree implements Runnable {
	public void run() {
		Hashtable<String, String> capsHashtable = new Hashtable<String, String>();
		capsHashtable.put("browser", "safari");
		capsHashtable.put("browser_version", "14");
		capsHashtable.put("os", "OS X");
		capsHashtable.put("os_version", "Big Sur");
		capsHashtable.put("build", "BStack-[Java] Sample Build");
		capsHashtable.put("name", "Thread 3");
		JavaSample deviceThree = new JavaSample();
		deviceThree.executeTest(capsHashtable);
	}
}
class DeviceFour implements Runnable {
	public void run() {
		Hashtable<String, String> capsHashtable = new Hashtable<String, String>();
		capsHashtable.put("browser", "Chrome");
		capsHashtable.put("browser_version", "latest");
		capsHashtable.put("os", "OS X");
		capsHashtable.put("os_version", "Monterey");
		capsHashtable.put("build", "BStack-[Java] Sample Build");
		capsHashtable.put("name", "Thread 4");
		JavaSample deviceFour = new JavaSample();
		deviceFour.executeTest(capsHashtable);
  	}
}
class DeviceFive implements Runnable {
	public void run() {
		Hashtable<String, String> capsHashtable = new Hashtable<String, String>();
		capsHashtable.put("browser", "Chrome");
		capsHashtable.put("browser_version", "latest");
		capsHashtable.put("os", "Windows");
		capsHashtable.put("os_version", "10");
		capsHashtable.put("build", "BStack-[Java] Sample Build");
		capsHashtable.put("name", "Thread 5");
		JavaSample deviceFive = new JavaSample();
		deviceFive.executeTest(capsHashtable);
  	}
}
public class JavaSample {
	public static final String USERNAME = "YOUR_USERNAME";
	public static final String AUTOMATE_KEY = "YOUR_ACCESS_KEY";
	public static final String URL = "https://" + USERNAME + ":" + AUTOMATE_KEY + "@hub-cloud.browserstack.com/wd/hub";
	public static void main(String[] args) throws Exception {
		Thread threadOne = new Thread(new DeviceOne());
		threadOne.start();
		Thread threadTwo = new Thread(new DeviceTwo());
		threadTwo.start();
		Thread threadThree = new Thread(new DeviceThree());
		threadThree.start();
		Thread threadFour = new Thread(new DeviceFour());
		threadFour.start();
		Thread threadFive = new Thread(new DeviceFive());
		threadFive.start();
		}
	public void executeTest(Hashtable<String, String> capsHashtable) {
		String key;
		DesiredCapabilities caps = new DesiredCapabilities();
		// Iterate over the hashtable and set the capabilities
		Set<String> keys = capsHashtable.keySet();
		Iterator<String> keysIterator = keys.iterator();
		while (keysIterator.hasNext()) {
			key = keysIterator.next();
			caps.setCapability(key, capsHashtable.get(key));
			}
		WebDriver driver;
		try {
			driver = new RemoteWebDriver(new URL(URL), caps);
			final JavascriptExecutor jse = (JavascriptExecutor) driver;
			try {
				// Searching for 'BrowserStack' on google.com
				driver.get("https://bstackdemo.com/");
				WebDriverWait wait = new WebDriverWait(driver, 10);
				wait.until(ExpectedConditions.titleIs("StackDemo"));
				// Getting name of the product
				String product_name = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id=\'1\']/p"))).getText();
				//checking whether the Add to Cart button is clickable
				WebElement cart_btn = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@id=\'1\']/div[4]")));
				// clicking the 'Add to cart' button
				cart_btn.click();
				// checking if the Cart pane is visible
				wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("float-cart__content")));
				// getting the product's name added in the cart
				final String product_in_cart = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id=\'__next\']/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]"))).getText();
				// checking if the product added to cart is available in the cart
				if (product_name.equals(product_in_cart)) {
					jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\": \"passed\", \"reason\": \"Product has been added to the cart!\"}}");
				}
			} catch (Exception e) {
				jse.executeScript("browserstack_executor: {\"action\": \"setSessionStatus\", \"arguments\": {\"status\": \"failed\", \"reason\": \"Some elements failed to load..\"}}");
			}
			driver.quit();
			} catch (MalformedURLException e) {
			e.printStackTrace();
		}
	}
}

Parallel tests using a multi-threaded program and ExecutorService

In case of the above implementation, you would have to manually keep track of which thread has completed its execution and depending on that, you will be required to spawn new threads keeping in mind you do not exceed the limit of parallel tests that is supported by your BrowserStack account.

You can choose to not manage the threads manually and use the Java ExecutorService to do the same for you.

Suppose you have a 2 parallel account and you have 20 tests to be run. You can create 20 different methods (similar to the 4 shown in the above example) and create a thread pool of 2 threads using Java ExecutorService as shown below:

// Import relevant packages here
public class ExecutorSample {
	public static final String USERNAME = "YOUR_USERNAME";
	public static final String AUTOMATE_KEY = "YOUR_ACCESS_KEY";
	public static final String URL = "https://" + USERNAME + ":" + AUTOMATE_KEY + "@hub-cloud.browserstack.com/wd/hub";
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService executorService = Executors.newFixedThreadPool(2);  // A pool of 2 threads are being created here. You can change this as per your parallel limit
		Set<Callable<String>> callables = new HashSet<Callable<String>>();
		ExecutorSample obj1 = new ExecutorSample();
		callables.add(new Callable<String>() {
		    public String call() throws Exception {
				Hashtable<String, String> capsHashtable = new Hashtable<String, String>();
				capsHashtable.put("device", "iPhone 12 Pro");
				capsHashtable.put("real_mobile", "true");
				capsHashtable.put("build", "BStack-[Java] Sample Build");
				capsHashtable.put("name", "Thread 1");
				obj1.executeTest(capsHashtable);
				return "Task 1 completed";
		    }
		});
		callables.add(new Callable<String>() {
		    public String call() throws Exception {
				Hashtable<String, String> capsHashtable = new Hashtable<String, String>();
				capsHashtable.put("device", "Samsung Galaxy S20");
				capsHashtable.put("real_mobile", "true");
				capsHashtable.put("build", "BStack-[Java] Sample Build");
				capsHashtable.put("name", "Thread 2");
				obj1.executeTest(capsHashtable);
				return "Task 2 completed";
		    }
		});
		callables.add(new Callable<String>() {
		    public String call() throws Exception {
				Hashtable<String, String> capsHashtable = new Hashtable<String, String>();
				capsHashtable.put("browser", "safari");
				capsHashtable.put("browser_version", "latest");
				capsHashtable.put("os", "OS X");
				capsHashtable.put("os_version", "Big Sur");
				capsHashtable.put("build", "BStack-[Java] Sample Build");
				capsHashtable.put("name", "Thread 3");
				obj1.executeTest(capsHashtable);
				return "Task 3 completed";
		    }
		});
		callables.add(new Callable<String>() {
		    public String call() throws Exception {
				Hashtable<String, String> capsHashtable = new Hashtable<String, String>();
				capsHashtable.put("browser", "chrome");
				capsHashtable.put("browser_version", "latest");
				capsHashtable.put("os", "Windows");
				capsHashtable.put("os_version", "10");
				capsHashtable.put("build", "BStack-[Java] Sample Build");
				capsHashtable.put("name", "Thread 4");
				obj1.executeTest(capsHashtable);
				return "Task 4 completed";
		    }
		});
		// You can add as many test functions as Callables as you want
		List<Future<String>> futures;
		futures = executorService.invokeAll(callables);
		for(Future<String> future : futures){
			System.out.println("future.get = " + future.get());
		}
		executorService.shutdown();
	}
	public void executeTest(Hashtable<String, String> capsHashtable) {
	  // The same test function as shown in the code snippet above
	}
}

Parallel tests using a shell script

Alternatively, you can also write Java classes with respect to each test that you wish to run. Each class then has to be compiled and then you can run the individual Java tests in parallel using a shell script as shown below:

javac testClass1.java
javac testClass2.java
javac testClass3.java
javac testClass4.java
javac testClass5.java
javac testClass6.java
# Compile all your java classes

java testClass1 &
java testClass2 &
java testClass3 &
java testClass4 &
java testClass5 &
java testClass6 &
Note: You have to ensure that you have added the external Selenium libraries (JARs) as downloaded from Selenium website, in your CLASSPATH during compiling and running your Java classes.

In the above shell script, all 6 tests will be executed in parallel, in their own processes.

Conclusion

If you are using one of the Java based test frameworks, then it is best to utilize the functionality of the framework to implement cross-browser testing or testing in parallel, in general. The first section of the document gives links to documentation for different popular frameworks.

And, if you are not using any framework, then 3 techniques are shown above for running Java tests in parallel. But it is not an exhaustive list. You may incorporate any method you like, to run your test code in parallel.

We're sorry to hear that. Please share your feedback so we can do better

Contact our Support team for immediate help while we work on improving our docs.

We're continuously improving our docs. We'd love to know what you liked






Thank you for your valuable feedback

Is this page helping you?

Yes
No

We're sorry to hear that. Please share your feedback so we can do better

Contact our Support team for immediate help while we work on improving our docs.

We're continuously improving our docs. We'd love to know what you liked






Thank you for your valuable feedback!

Talk to an Expert
Talk to an Expert