Parallel Testing in Cucumber with JUnit

HOME

In this tutorial, I will explain Parallel Testing using Cucumber with JUnit.

Cucumber-JVM allows parallel execution across multiple threads since version 4.0.0. There are several options to incorporate this built-in feature in a Cucumber project. You can do so by using JUnit, TestNG or CLI.

Cucumber can be executed in parallel using JUnit and Maven test execution plugins.

In JUnit, the feature files are run in parallel rather than scenarios, which means all the scenarios in a feature file will be executed by the same thread. You can use either Maven Surefire or Failsafe plugin to execute the runner. In this tutorial, I’m using the Maven Surefire plugin.

This framework consists of:-

  1. Cucumber Java- 6.8.1
  2. Cucumber JUnit – 6.8.1
  3. Java 11
  4. JUnit– 4.13.2
  5. Maven – 3.8.1
  6. Selenium – 3.141.59
  7. Maven Surefire Plugin – 3.0.0-M5

Steps to create a project for parallel Testing in Cucumber

  1. Create a Maven project.
  2. Add Cucumber and JUnit dependencies to the project.
  3. Add Surefire plugin configuration to the build section to the POM.
  4. Create a feature file under src/test/resources.
  5. Create the Step Definition class or Glue Code.
  6. Create a Cucumber Runner class.
  7. Execute the test from Command Line.
  8. Generate Cucumber Report.

Detailed Step Description

Step 1 – Create a Maven project

Create a Maven project in your favorite IDE using the cucumber-archetype or by adding Cucumber dependencies to the POM as detailed here and Junit dependencies here. To know more about this, click here.

Below is the structure of project.

Step 2 – Update Properties section in Maven pom.xml

<properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   <selenium.version>3.141.59</selenium.version> 
   <cucumber.version>6.8.1</cucumber.version>
   <junit.version>4.13.2</junit.version>
   <maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
 </properties>

Step 2 – Add Cucumber and JUnit dependencies to the project

Add below mentioned Cucumber-Java and Cucumber-JUnit dependencies to the project.

<dependencies>

   <!-- Cucumber Dependencies -->
    <dependency>
      <groupId>io.cucumber</groupId>
      <artifactId>cucumber-java</artifactId>
      <version>${cucumber.version}</version>
    </dependency>
  
    <dependency>
      <groupId>io.cucumber</groupId>
      <artifactId>cucumber-junit</artifactId>
      <version>${cucumber.version}</version>
      <scope>test</scope>
     </dependency>

   <!-- JUnit Dependency -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
   </dependency>

    <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>${selenium.version}</version>
    </dependency

</dependencies>
  

Step 3 – Add Surefire plugin configuration to the byuld section to the POM.

 <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>${maven.surefire.plugin.version}</version>
   <configuration>
        <parallel>methods</parallel>
        <useUnlimitedThreads>true</useUnlimitedThreads>
        <testFailureIgnore>true</testFailureIgnore>
	</configuration>
</plugin> 

To set the thread count to a specific number instead of useUnlimitedThreads use the below setting.

<configuration>
    <parallel>methods</parallel>
    <threadCount>4</threadCount>
</configuration>

The thread count in the above setting is 4 threads per core.

Step 4 – Create a feature folder under src/test/resources

Add 4 feature files – FlightBooking.feature, TrainBooking.feature, HomePage.feature and Login.feature

FlightBooking.feature

Feature: Book flight ticket 
 
@BookOneWayFlight
Scenario: Book Flight for one way trip
 
Given I want to book one way flight ticket from Dublin to London on 22nd July for 2 adults and 2 kids
Then TripAdvisor should provide me options to book flight ticket

@BookRoundTripFlight
Scenario: Book Flight for round trip
 
Given  I want to book roundtrip flight ticket from Dublin to India on 30th June and return 10th July for 2 adults and 1 kid
Then TripAdvisor should provide me options to book flight ticket 

TrainBooking.feature

Feature: Book Train Ticket
 
@BookOneWayTrain
Scenario: Book train ticket for one way
 
Given I want to book one way train ticket from Dublin to Cork on 10th June for 2 adults and 2 kids
Then IrishRail should provide me options to book train ticket for the specified date

HomePage.feature

Feature: Home page validation
  
Background:
   Given User Navigates to HRM login page
   And User login with valid credentials
 
   @ValidQuickLaunch
   Scenario Outline: Login with valid credentials to check QuickLanuch options  - Feature 2, Scenario -1
     
   When User is in Dashboard page
     Then there are valid QuickLaunch options '<options>'
         
    Examples: 
        |options                  |
        |Assign Leave             |
        |Leave List               |
 
     
    @ValidLegendOptions    
    Scenario Outline: Login with valid credentials to check Manu Options - Feature 2, Scenario -2
     
   When User is in Dashboard page
     Then there are valid Legend options '<legendOptions>'
         
    Examples: 
        |legendOptions               |
        |Administration              |

Login.feature

Feature: Login to HRM Application 
  
   @ValidCredentials
   Scenario: Login with valid credentials - Feature 1, Scenario -1
      
    Given User is on Home page
    When User enters username as "Admin"
    And User enters password as "admin123"
    Then User should be able to login sucessfully

Step 5 – Create the Step Definition class or Glue Code

Below is the Step Definition for FlightBooking.feature.

public class FlightBookingDefinitions {
	
	@Given("I want to book one way flight ticket from Dublin to London on 22nd July for 2 adults and 2 kids")
	   public void singleTrip(){
	        System.out.println("I want to book one way flight ticket from Dublin to London on 22nd July for 2 adults and 2 kids");
	   }
	 
	@Then("TripAdvisor should provide me options to book flight ticket")
	   public void TripAdvisor(){
	        System.out.println("TripAdvisor should provide me options to book flight ticket");
	   }
	
	@Given("I want to book roundtrip flight ticket from Dublin to India on 30th June and return 10th July for 2 adults and 1 kid")
	   public void roundTrip(){
	        System.out.println("I want to book one way flight ticket from Dublin to London on 22nd July for 2 adults and 2 kids");
	   }
}

Below is the StepDefinition for TrainBooking.feature.

public class TrainBookingDefinitions {

	@Given("I want to book one way train ticket from Dublin to Cork on 10th June for 2 adults and 2 kids")
	   public void hotelWithoutBreakfast(){
	        System.out.println("I want to book one way train ticket from Dublin to Cork on 10th June for 2 adults and 2 kids");
	   }
	 
	@Then("IrishRail should provide me options to book train ticket for the specified date")
	   public void Trivago(){
	        System.out.println("IrishRail should provide me options to book train ticket for the specified date");
	   }

}

Below is the StepDefinition for Login.feature.

public class LoginDefinition {

	WebDriver driver;

	@Given("User is on Home page")
	public void userOnHomePage() {

		System.setProperty("webdriver.gecko.driver",
				"C:\\Users\\Vibha\\Software\\geckodriver-v0.26.0-win64\\geckodriver.exe");

		driver = new FirefoxDriver();

		driver.manage().window().maximize();
		driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

		driver.get("https://opensource-demo.orangehrmlive.com/");
	}

	@When("User enters username as {string}")
	public void entersUsername(String userName) throws InterruptedException {

		System.out.println("Username Entered");
		driver.findElement(By.name("txtUsername")).sendKeys(userName);

	}

	@When("User enters password as {string}")
	public void entersPassword(String passWord) throws InterruptedException {

		System.out.println("Password Entered");
		driver.findElement(By.name("txtPassword")).sendKeys(passWord);

		driver.findElement(By.id("btnLogin")).submit();
	}

	@Then("User should be able to login sucessfully")
	public void sucessfullLogin() throws InterruptedException {

		String newPageText = driver.findElement(By.id("welcome")).getText();
		System.out.println("newPageText :" + newPageText);
		Assert.assertTrue(newPageText.contains("Welcome"));

		driver.quit();

	}
}

Below is the StepDefinition for HomePage.feature.

public class HomePageDefinition {

	WebDriver driver;

	@Given("User Navigates to HRM login page")
	public void userOnHomePage() {

		System.setProperty("webdriver.gecko.driver",
				"C:\\Users\\Vibha\\Software\\geckodriver-v0.26.0-win64\\geckodriver.exe");

		driver = new FirefoxDriver();
		driver.manage().window().maximize();
		driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
		driver.get("https://opensource-demo.orangehrmlive.com/");
	}

	@Given("User login with valid credentials")
	public void entersCredentials() throws InterruptedException {
		Thread.sleep(1000);

		driver.findElement(By.name("txtUsername")).sendKeys("Admin");
		driver.findElement(By.name("txtPassword")).sendKeys("admin123");
		driver.findElement(By.id("btnLogin")).submit();

	}

	@When("User is in Dashboard page")
	public void verifyDashboardPage() {

		String dashboardTitle = driver.findElement(By.id("welcome")).getText();
		Assert.assertTrue(dashboardTitle.contains("Welcome"));

	}

	@Then("there are valid QuickLaunch options {string}")
	public void verifyQuickLinks(String options) throws InterruptedException {

		switch (options) {
		case "Assign Leave":
			String linkOne = driver
					.findElement(By.xpath(
							"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[1]/div/a/span"))
					.getText();
			Assert.assertEquals(linkOne, options);
			Thread.sleep(1000);
			break;
		case "Leave List ":
			String linkTwo = driver
					.findElement(By.xpath(
							"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[2]/div/a/span"))
					.getText();
			Assert.assertEquals(linkTwo, options);
			Thread.sleep(1000);
			break;
		case "Timesheets":
			String linkThree = driver
					.findElement(By.xpath(
							"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[3]/div/a/span"))
					.getText();
			Assert.assertEquals(linkThree, options);
			Thread.sleep(1000);
			break;
		default:
			break;
		}

		driver.quit();

	}

	@Then("there are valid Legend options {string}")
	public void verifyMenuOptions(String options) throws InterruptedException {

		switch (options) {
		case "Not assigned to Subunits":
			String linkOne = driver
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[1]/td[2]"))
					.getText();
			Assert.assertEquals(linkOne, options);
			Thread.sleep(1000);
			break;
		case "Administration":
			String linkTwo = driver
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[2]/td[2]"))
					.getText();
			Assert.assertEquals(linkTwo, options);
			Thread.sleep(1000);
			break;
		case "Client Services":
			String linkThree = driver
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[3]/td[2]"))
					.getText();
			Assert.assertEquals(linkThree, options);
			Thread.sleep(1000);
			break;
		default:
			break;

		}
		driver.quit();
	}
}

Step 6 – Create a Test Runner to run the tests

import org.junit.runner.RunWith;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)

@CucumberOptions(features= {"src/test/resources"}, glue= {"com.cucumber.parallel_demo"})
public class RunCucumberTest {

}

Step 7 – Execute the tests from command line

mvn test

Below is the execution screen. There are four feature files. Out of four files, two are simple feature files (FlightBooking and TrainBooking ) whereas another two Feature files are complex (Login and Home). These feature file contains the below number of scenarios.

FlightBooking – 2 Scenarios
TrainBooking – 1 Scenario
Login – 1 Scenario
Home – 4 Scenarios

Feature files are executed in alphabetical order. So, the sequence in which non-parallel tests will run is FlightBooking -> Home ->Login -> TrainBooking

But, as we have executed tests parallelly, so the feature files will run in this order (depending on the least number of scenarios will be executed first) TrainBooking -> FlightBooking -> Login -> Home

All the tests of a particular feature file are executed together as feature files are run in parallel, not scenarios.

Step 8 – Cucumber Report Generation

To get Cucumber Test Reports, add cucumber.properties under src/test/resources and add the below instruction in the file.

cucumber.publish.enabled=true

Below is the image of the Cucumber Report generated using Cucumber Service.

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

In the next tutorial, I have explained about Parallel Testing in Cucumber with TestNG.

One thought on “Parallel Testing in Cucumber with JUnit

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s