Advanced Testing Parallelisation Technique for DevOps Pipeline using Dockers

To achieve the parallel execution of Scenarios, we need to do several tweaks to our TestNG-based Selenium Test Automation Framework by using ThreadSafe & ThreadLocal<RemoteDriver>.

Firstly, download the Maven dependency by using below code –

<dependency>
	<groupId>io.github.bonigarcia</groupId>
	<artifactId>webdrivermanager</artifactId>
	<version>3.0.0</version>
</dependency>

Create a TestNG Project and add a TestNG Test Class like below –

LaunchAppTests.java

package com.docker.tests;

import org.openqa.selenium.By;
import org.testng.Assert;
import org.testng.AssertJUnit;
import org.testng.annotations.Test;

import com.docker.utils.Hooks;

public class LaunchAppTests extends Hooks {

	@Test
	public void GoogleLaunchTest() throws InterruptedException {

		System.out.println("Thread Id: " + Thread.currentThread().getId());

		getDriver().navigate().to("http://www.google.com");

		Assert.assertEquals(getDriver().getTitle(), "Google");
	}

	@Test
	public void SauceLabDemoTest() throws InterruptedException {

		System.out.println("Thread Id: " + Thread.currentThread().getId());

		getDriver().navigate().to("https://www.saucedemo.com/index.html");

		String title = getDriver().getTitle();

		System.out.println("Page Title: " + title);

		AssertJUnit.assertEquals(title, "Swag Labs");
	}

	@Test
	public void BBCLaunchTest() {
		
		System.out.println("Thread Id: " + Thread.currentThread().getId());

		getDriver().navigate().to("https://www.bbc.co.uk/");

		String title = getDriver().getTitle();

		System.out.println("Page Title: " + title);

		AssertJUnit.assertEquals(title, "BBC - Home");
		
	}
	
	
	public void SauceLabLoginTest() throws InterruptedException {

		System.out.println("Thread Id: " + Thread.currentThread().getId());

		getDriver().navigate().to("https://www.saucedemo.com/index.html");

		getDriver().findElement(By.id("user-name")).sendKeys("standard_user");

		getDriver().findElement(By.id("password")).sendKeys("secret_sauce");

		getDriver().findElement(By.id("user-name")).submit();

		getDriver().findElement(By.xpath("//*[@id=\"menu_button_container\"]/div/div[3]/div/button")).click();

		getDriver().findElement(By.linkText("Logout")).click();
	}
}

Create another new class named – Hooks.java

The technique to execute the Cucumber Scenarios lies with ThreadSafe Tests

package com.docker.utils;

import java.net.MalformedURLException;
import java.net.URL;

import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
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 Hooks {

	// Declare ThreadLocal Driver (ThreadLocalMap) for ThreadSafe Tests

	protected static ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<>();

	public Capabilities capabilities;

	@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"), 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();
	}

	private Capabilities getCapabilities(String browser) {

		if (browser.equals("firefox"))

			capabilities = getFirefoxOptions();

		else

			capabilities = getChromeOptions();

		return capabilities;

	}

	private ChromeOptions getChromeOptions() {

		ChromeOptions options = new ChromeOptions();

		return options;
	}

	private FirefoxOptions getFirefoxOptions() {

		FirefoxOptions options = new FirefoxOptions();

		FirefoxProfile profile = new FirefoxProfile();

		profile.setPreference("network.proxy.type", 0);

		options.setCapability(FirefoxDriver.PROFILE, profile);

		return options;
	}
}

Time to create a TestNG.xml file to invoke the tests

<?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="10">
		<parameter name="browser" value="chrome" />
		<classes>
			<class name="com.docker.tests.LaunchAppTests">
			</class>
		</classes>
	</test>
</suite> 

Now, to run the tests in Parallel in an effective way, we need to install and configure Selenium Grid. To do that I use the below docker-compose.xml file

version: '3'
services:

  selenium-hub:
    image: selenium/hub
    ports:
      - "4444:4444"
    environment:
        GRID_MAX_SESSION: 15
        GRID_BROWSER_TIMEOUT: 300
        GRID_TIMEOUT: 300

  chrome:
    image: selenium/node-chrome
    depends_on:
      - selenium-hub
    environment:
      HUB_PORT_4444_TCP_ADDR: selenium-hub
      HUB_PORT_4444_TCP_PORT: 4444
      NODE_MAX_SESSION: 2
      NODE_MAX_INSTANCES: 2

Run the below command to bring up the Selenium Grid and Nodes

docker-compose up

Now, it’s time to run the TestNG.xml file. Once triggered, the Scenarios are parallelised and they run in parallel in the Selenium Grid in a Headless mode with the sessions allocated automatically.

Image Credits: https://www.linuxnix.com/join-devops-whatsapp-groups/

Comments are closed.

Powered by WordPress.com.

Up ↑

%d bloggers like this: