Hi, In this post, we will see how to run cucumber scenarios in parallel with TestNG.
For the demonstration purposes, I kept the project structure simple, one package for all classes and a folder for scenarios.
Below is how it looks like in Eclipse.
Watch the no voice demo at : https://youtu.be/IkrXOkkDG5c
Codebase:
Software setup :
- java version "1.8.0_371"
Java(TM) SE Runtime Environment (build 1.8.0_371-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.371-b11, mixed mode)
- Chrome Browser Version 114.0.5735.110 (Official Build) (64-bit)
- Chrome WebDriver version :
https://chromedriver.storage.googleapis.com/index.html?path=114.0.5735.90/ - pom.xml dependencies/plugins
- cucumber-java 7.7.0
- cucumber-testng 7.7.0
- selenium-java 4.10.0
- webdrivermanager 5.3.3
- maven-surefire-plugin 3.1.2
1) Verify user is able to login to the application with valid credentials
2) Verify Forgot password link is displayed on login page.
- Dependencies/plugins in pom.xml
- Basic DriverFactory for chrome driver with ThreadLocal
- POM model with page factory pattern.
- Parallel execution configurations
- Cucumber hooks
- Is it necessary to have testng.xml for parallel execution if the code run from eclipse ?
1) GitHub
2) Google Drive
You could also walk through the no voice video tutorial of the same demo at
Deep dive into thread local look at these.
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ThreadLocal.html
From Java Point:
https://www.javatpoint.com/java-threadlocal-class
- Add all aforementioned dependencies/plugins in pom.xml file. In the maven-surefire-plugin configure parallel test with methods and number of threads to open when run automation.
- Define the DriverFactory class with ThreadLocal web driver and then set the driver, get the driver, quit the driver with ThreadLocal class methods. This is the major aspect of localizing thread for step definitions.
privatestatic ThreadLocal<WebDriver> driver = new ThreadLocal<>();
- Create cucumber runner class, in this write the piece of parallel testing. Making true enables the parallel testing and false to normal/sequential tests.
@Override
@DataProvider(parallel = true)
public Object[][] scenarios() {
returnsuper.scenarios();
}
- It is always an automation best practice to keep the tests independent of one another, especially my experience with cucumber is to use Before and After hooks efficiently to trigger the browser instance and quit it when completes. I vote cucumber hooks over TestNG hooks with cucumber integration with TestNG.
@Before(order=0)
publicvoid before(Scenario scenaio) throws Exception{
DriverFactory.setDriver();
System.out.println("Current Thread Name:"+Thread.currentThread().getName());
System.out.println("Current Thread ID:"+Thread.currentThread().getId());
}
@After(order=0)
publicvoid after() throws Exception{
DriverFactory.closeDriver();
}
- Login to the application , Forgot Password resides on same Login page, so single feature is good enough to have this for Gherkin
Login.feature#Author: Sadakar Pochampalli
@LoginFeature
Feature: Login page validations
Background:
Given User is on login page
@Login
Scenario: Login to Orange HRM
When User enters username "Admin"
And User enters password "Admin123"
And User clicks on Login button
Then User should navigate to Orange HRM home page
@ForgotPassword
Scenario: Forgot password link verification
Then User verifies Forgot password link display
- Now, it's the Page Object Model with page factory pattern. (Refer bottom section for complete code)
LoginPageFactory.javapublic WebDriver driver;
public LoginPageFactory(WebDriver driver) {
this.driver = driver;
}
// Username locator
@FindBy(xpath = "//input[@name='username']")
public WebElement userName;
Initialize the elements in the step definition. (Refer bottom section for complete code)
LoginStepDef.javaLoginPageFactory login = PageFactory.initElements(DriverFactory.getDriver(), LoginPageFactory.class);
@Given("User is on login page")
publicvoid user_is_on_login_page() throws InterruptedException {
DriverFactory.getDriver().get("https://opensource-demo.orangehrmlive.com/web/index.php/auth/login");
Thread.sleep(3000);
}
@When("User enters username {string}")
publicvoid user_enters_username(String username) {
login.enterUsername(username);
}
<projectxmlns="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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>CucumberParallelExecutionTestNG</groupId><artifactId>CucumberParallelExecutionTestNG</artifactId><version>0.0.1-SNAPSHOT</version><name>CucumberParallelExecutionTestNG</name><description>CucumberParallelExecutionTestNG</description><dependencies><dependency><groupId>io.cucumber</groupId><artifactId>cucumber-java</artifactId><version>7.7.0</version></dependency><!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java --><dependency><groupId>io.cucumber</groupId><artifactId>cucumber-testng</artifactId><version>7.7.0</version></dependency><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.10.0</version></dependency><!-- https://mvnrepository.com/artifact/org.testng/testng --><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>7.7.0</version></dependency><dependency><groupId>io.github.bonigarcia</groupId><artifactId>webdrivermanager</artifactId><version>5.3.3</version></dependency><dependency><groupId>tech.grasshopper</groupId><artifactId>extentreports-cucumber7-adapter</artifactId><version>1.2.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version><scope>provided</scope></dependency><!-- https://mvnrepository.com/artifact/javax.mail/mail --><dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>1.4.7</version></dependency></dependencies><build><pluginManagement><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.1.2</version><configuration><parallel>methods</parallel><useUnlimitedThreads>2</useUnlimitedThreads><suiteXmlFiles><suiteXmlFile>testng.xml</suiteXmlFile></suiteXmlFiles></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.7.0</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></pluginManagement></build></project>
package parallel;importorg.testng.annotations.DataProvider;importio.cucumber.testng.AbstractTestNGCucumberTests;importio.cucumber.testng.CucumberOptions;@CucumberOptions( features ="classpath:parallel", tags ="@Login or @ForgotPassword", glue ={"parallel"}, plugin={"pretty","json:target/cucumber-reports/cucumber.json","html:target/cucumber-reports/cucucmber-report.html"}, monochrome=true)publicclassCucumberRunnerTestextends AbstractTestNGCucumberTests {@Override@DataProvider(parallel =true)public Object[][]scenarios(){returnsuper.scenarios();}}
package parallel;importorg.openqa.selenium.WebDriver;importorg.openqa.selenium.chrome.ChromeDriver;importorg.openqa.selenium.chrome.ChromeOptions;importio.github.bonigarcia.wdm.WebDriverManager;publicfinalclassDriverFactory{privatestatic ThreadLocal<WebDriver> driver =new ThreadLocal<>();publicstaticvoidsetDriver(){ ChromeOptions options =new ChromeOptions(); options.addArguments("--remote-allow-origins=*");//options.addArguments("--headless"); WebDriverManager.chromedriver().setup(); driver.set(new ChromeDriver(options));}publicstatic WebDriver getDriver(){return driver.get();}publicstaticvoidcloseDriver(){ driver.get().quit(); driver.remove();}}
package parallel;importio.cucumber.java.After;importio.cucumber.java.Before;importio.cucumber.java.Scenario;publicclassHooks{@Before(order=0)publicvoidbefore(Scenario scenaio)throws Exception{ DriverFactory.setDriver(); System.out.println("Current Thread Name:"+Thread.currentThread().getName()); System.out.println("Current Thread ID:"+Thread.currentThread().getId());}@After(order=0)publicvoidafter()throws Exception{ DriverFactory.closeDriver();}}
package parallel;importorg.openqa.selenium.WebDriver;importorg.openqa.selenium.WebElement;importorg.openqa.selenium.support.FindBy;publicclassLoginPageFactory{public WebDriver driver;publicLoginPageFactory(WebDriver driver){this.driver= driver;}// Username locator@FindBy(xpath ="//input[@name='username']")public WebElement userName;// Password locator@FindBy(xpath ="//input[@name='password']")public WebElement passWord;// Login button locator@FindBy(xpath ="//button[@type='submit']")public WebElement loginButton;// Forgot password locator@FindBy(xpath ="//p[@class='oxd-text oxd-text--p orangehrm-login-forgot-header']")public WebElement forgotPassword;// Method that performs Username actionpublicvoidenterUsername(String uname){ userName.sendKeys(uname);}// Method that performs Password actionpublicvoidenterPassword(String pwd){ passWord.sendKeys(pwd);}// Method that performs Login actionpublicvoidclickLogin(){ loginButton.click();}// Method that performs Forgot Password link verificationpublicbooleanisForgotPasswordLinkPresent(){return forgotPassword.isDisplayed();}}
package parallel;importorg.openqa.selenium.support.PageFactory;importorg.testng.Assert;importio.cucumber.java.en.Given;importio.cucumber.java.en.Then;importio.cucumber.java.en.When;publicclassLoginStepDef{ LoginPageFactory login = PageFactory.initElements(DriverFactory.getDriver(), LoginPageFactory.class);@Given("User is on login page")publicvoiduser_is_on_login_page()throws InterruptedException { DriverFactory.getDriver().get("https://opensource-demo.orangehrmlive.com/web/index.php/auth/login"); Thread.sleep(3000);}@When("User enters username {string}")publicvoiduser_enters_username(String username){ login.enterUsername(username);}@When("User enters password {string}")publicvoiduser_enters_password(String password){ login.enterPassword(password);}@When("User clicks on Login button")publicvoiduser_clicks_on_login_button(){ login.clickLogin();}@Then("User should navigate to Orange HRM home page")publicvoiduser_should_navigate_to_orange_hrm_home_page(){ System.out.println("Navigation to home page");}@Then("User verifies Forgot password link display")publicvoiduser_verifies_forgot_password_link_display(){ Assert.assertTrue(login.isForgotPasswordLinkPresent());}}
Login.feature
#Author: Sadakar Pochampalli@LoginFeatureFeature: Login page validationsBackground: Given User is on login page@LoginScenario: Login to Orange HRM When User enters username "Admin"And User enters password "Admin123"And User clicks on Login buttonThen User should navigate to Orange HRM home page@ForgotPasswordScenario: Forgot password link verification Then User verifies Forgot password link display
Is your latest cucumber/extent report is not attached in the mail notification ? will see you in post with the tip on this.