Hi, In this post, we'll learn about how to integrate Cucumber with Selenium, TestNG and Maven.
This is as part of a project that we wanted to migrate the UI automation from Junit frame work to TestNG and we also wanted to upgrade to the latest versions of the dependencies.
For the demo, we will use the HRM website.
https://opensource-demo.orangehrmlive.com/index.php/auth/login
Username : Admin, Password: admin123
Let's take the following use case of scenario's.
Test Cases/Scenarios:
Verify log-in page with valid credentials
Display the size of quick launch elements Dashboard page/tab
Directory tab navigation from Dashboard page/tab
Verify Search button is displayed or not in Dictionary page/tab
In this framework, we will see the below technical implementations of Cucumber, Selenium and TestNG
Cucumber features:
- How to implement data driven approach using DataTable object in Login scenario ?
- How to use Background keyword in feature files to to login for each scenario except for Login?
(keep the tests independent of each other) - How to give Tags(single and multiple tags) in CucumberOptions in RunCucumberTest class?
- How to use Cucumber Before and After Hooks for each scenario ?
- Online cucumber report
Selenium features:
- Implicit wait with Duration of times
- Explicit wait with Duration of times
- findElements , add elments to List<WebElement>, display the list elements, size of the list
TestNG and Maven features:
- Assertions used in this demo are from TestNG framework.
- How to configure testng.xml file in pom.xml in the maven-surefire-plugin ?
- How to run the cucumber scenarios from command line ?
Let's begin!
The first and foremost thing, I've come across is to create the proper maven project structure.
i.e., the usage of src/test/java and src/test/resources
i.e., the usage of src/test/java and src/test/resources
We can keep our source code in src/main/java and src/main/resources but in order to run the scenarios from maven command line, this cucumber-java-skeleton is recommended to keep the code in
src/test/java and src/test/resources - at least for the RunnerTest classes so the maven sure fire plug-in identifies the code from test
Having walk-through above all, here we start with what versions this writing consists of!
- cucumber-java 7.1.0
- cucumber-testng 7.1.0
- selenium 4.3.0
- testng 7.1.0
- maven-surefire-plugin 3.0.0-M7
- maven-compiler-plugin 3.10.1
- Maven installed in Windows is 3.8.6
Now, the steps:
- Download and install Cucumber plug-in in Eclipse from Market Place
- Download and install TestNG plug-in in Eclipse from Market Place
- Create a new Maven project (say : CucucumberTestNGSeleniumMavenCommandLine)
- Add cucumber-java, cucumber-testng, testng and selenium dependencies in the pom.xml and etc.
- Add Maven compiler plugin, maven-surefire-plugin in pom.xml
- Create feature files(Gherkhin script) in src/test/resources folder
- Write java, selenium, glue(step definitions), cucumber testng runner class in src/test/java folder
- BasePage class for driver
- Hooks class for cucumber Before and After hooks.
- Step Definition Or Glue Code for the feature files
- Cucumber & TestNG runner class, RunCucumberTest.java
- Create testng.xml for the project and configure it in pom.xml
- Run Tests from TestNG Tests
- Run Tests from testng.xml
- Run Tests from command line
- Test results analysis from Cucumber report
- Test results analysis from TestNG report
Step 1: Download and install Cucumber plug-in in Eclipse from Market Place
Install the cucumber plug in from the market place.
Step 2: Download and install TestNG plug-in in Eclipse from Market Place
Install the cucumber plug in from the market place.
Step 3: Download and install TestNG plug-in in Eclipse from Market Place
Refer to the above image : Project Structure (It should be a maven project)Step 4: Add cucumber-java, cucumber-testng, testng and selenium dependencies in the pom.xml and etc. and
Step 5: Add Maven compiler plugin, maven-surefire-plugin in pom.xml
Add the dependencies as shown in below pom.xml
<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>Cucumber7-TestNG7-Selenium4-OrnageHRM-POC2</groupId>
<artifactId>Cucumber7-TestNG7-Selenium4-OrnageHRM-POC2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-testng -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>7.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.testng/testng -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-compiler-plugin -->
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Step 6: Create feature files(Gherkhin script) in src/test/resources folder
The following scenarios are verified in this demo. This demo is designed to login for each scenario so the login step is used as Background step in the Dashbaord.feature and Directory.feature.
HRMLogin.feature
@HRMLogin
Feature: Login to HRM Application
I want to use this template for HRM Login page
@LoginValidCredentials
Scenario: LoginValidCredentials
Given User login to HRM application with UserName and Password
| Admin | admin123 |
Then User navigates to Dashboard page
Dashboard.feature
@Dasbhoard
Feature: Dashboard page
I want to use this template for my Dashboard Page
Background:
Given User login to HRM application with UserName and Password
| Admin | admin123 |
@DashboardTabCountOfQuickLaunhElements
Scenario: DashboardTabCountOfQuickLaunhElements
Then User finds the list of quick launch elements
@DirectoryTabNavigationFromDashboardTab
Scenario: DirectoryTabNavigationFromDashboardTab
Then User clicks on Directory tab and verifies the navigation
@Directory
Feature: Dashboard page
I want to use this template for my Directory Page
Background:
Given User login to HRM application with UserName and Password
| Admin | admin123 |
@DirectoryTabIsSearchButtonDisplayed
Scenario: DirectoryTabIsSearchButtonDisplayed
Then User is on Directory page
Then Is Search button displayed
Step 7: Write java, selenium, glue(step definitions), cucumber testng runner class in src/test/java folder
BasePage class for driver
Define the web driver in this call and use it as super class for the step definition classes or in the Hooks class.
Define the web driver in this call and use it as super class for the step definition classes or in the Hooks class.
Hooks class for cucumber Before and After hooks.
In the Before Hook, get the login page.
Since it is a repeated activity for each scenario, we keep it in this Hook which means that the code that we write in Before Hook is executed/called before the execution of each scenario.
We can do this in Background band in the feature files as well.
Since it is a repeated activity for each scenario, we keep it in this Hook which means that the code that we write in Before Hook is executed/called before the execution of each scenario.
We can do this in Background band in the feature files as well.
Use Before Hook and Background wisely.
Step Definition Or Glue Code for the feature files
Write the glude code or step definitions code for each scenario in the HRMLoginPage.java, Dashboard.java and Directory.java files.
iWe can rename the method names to avoid lengthy names that are generated through Feature run.
Cucumber & TestNG runner class, RunCucumberTest.java
Runner class should end with Test because maven doesn't identify the cucumber testng integration while executing from command line.
We can take any name for the runner class but should end with Test, in this case it is : RunCucumberTest.java
RunCucumberTest class should extends AbstractTestNGCucumberTests .
The latest version of the cucumber testng accept tags with and or not
For example, below statement executes two scenaro @LoginValidCredentials and @DirectoryTabIsSearchButtonDisplayed
tags="@LoginValidCredentials and not @DashboardTabCountOfQuickLaunhElements
and not @DirectoryTabNavigationFromDashboardTab
or @DirectoryTabIsSearchButtonDisplayed",
BasePage.java
package com.sadakar.common;
importorg.openqa.selenium.WebDriver;
publicclassBasePage{
publicstatic WebDriver driver;
}
Hooks.java
package com.sadakar.common;
importorg.openqa.selenium.chrome.ChromeDriver;
importio.cucumber.java.After;
importio.cucumber.java.Before;
publicclassHooksextends BasePage {
@Before
publicstaticvoidsetupDriver()throws InterruptedException {
System.setProperty("webdriver.chrome.driver","D:\\chromedriver.exe");
driver =new ChromeDriver();
driver.manage().window().maximize();
driver.get("https://opensource-demo.orangehrmlive.com/index.php/auth/login");
}
@After
publicstaticvoidquitDriver()throws Exception {
driver.quit();
}
}
HRMLoginPage.java
package com.sadakar.stepdefinitions;
importjava.time.Duration;
importjava.util.List;
importorg.openqa.selenium.By;
importorg.openqa.selenium.WebElement;
importorg.openqa.selenium.support.ui.ExpectedConditions;
importorg.openqa.selenium.support.ui.WebDriverWait;
importorg.testng.Assert;
importcom.sadakar.common.BasePage;
importio.cucumber.java.en.Given;
importio.cucumber.java.en.Then;
publicclassHRMLoginPageextends BasePage {
@Given("User login to HRM application with UserName and Password")
publicvoidloginToHRMApp(io.cucumber.datatable.DataTable dataTable){
List<List<String>> cells = dataTable.cells();
driver.findElement(By.xpath("//*[@id=\"txtUsername\"]")).sendKeys(cells.get(0).get(0));
driver.findElement(By.xpath("//*[@id=\"txtPassword\"]")).sendKeys(cells.get(0).get(1));
driver.findElement(By.id("btnLogin")).submit();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
}
@Then("User navigates to Dashboard page")
publicvoidnavigateToDashboardTab(){
WebDriverWait wait =new WebDriverWait(driver,Duration.ofSeconds(10));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id=\"menu_dashboard_index\"]")));
WebElement dashboardLabel = driver.findElement(By.xpath("//*[@id=\"content\"]/div/div[1]/h1"));
Assert.assertTrue(dashboardLabel.isDisplayed());
}
}
DashboardPage.java
package com.sadakar.stepdefinitions;
importjava.time.Duration;
importjava.util.List;
importorg.openqa.selenium.By;
importorg.openqa.selenium.WebElement;
importcom.sadakar.common.BasePage;
importio.cucumber.java.en.Then;
publicclassDashboardPageextends BasePage {
@Then("User finds the list of quick launch elements")
publicvoidlistOfQuickLaunchElementsOnDashboardPage(){
// Adding table data of a row to WebElement List
List<WebElement> actualListOfQuickLaunchElements = driver
.findElements(By.xpath("//*[@id=\"dashboard-quick-launch-panel-menu_holder\"]/table/tbody/tr/td"));
// Display the table data of row from the WebElementList
for(WebElement ele : actualListOfQuickLaunchElements){
System.out.println(ele.getText());
}
// Display the size of WebElement List
System.out.println("Size of Quick launch elements : "+ actualListOfQuickLaunchElements.size());
}
@Then("User clicks on Directory tab and verifies the navigation")
publicvoidnavigateToDirectoryTabFromDashbaordTab(){
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
driver.findElement(By.xpath("//*[@id=\"menu_directory_viewDirectory\"]")).click();
}
}
DirectoryPage.java
package com.sadakar.stepdefinitions;
importjava.time.Duration;
importorg.openqa.selenium.By;
importorg.openqa.selenium.WebElement;
importorg.testng.Assert;
importcom.sadakar.common.BasePage;
importio.cucumber.java.en.Then;
publicclassDirectoryPageextends BasePage{
@Then("User is on Directory page")
publicvoiddirectoryPage(){
driver.findElement(By.xpath("//*[@id=\"menu_directory_viewDirectory\"]")).click();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
}
@Then("Is Search button displayed")
publicvoidisSearchButtonDisplayed(){
WebElement searchButton = driver.findElement(By.xpath("//*[@id=\"searchBtn\"]"));
Assert.assertTrue(searchButton.isDisplayed());
}
}
RunCucumberTest.java
package com.sadakar.testng.runner;
importio.cucumber.testng.AbstractTestNGCucumberTests;
importio.cucumber.testng.CucumberOptions;
@CucumberOptions(
//tags="@LoginValidCredentials",
//tags="@DashboardTabCountOfQuickLaunhElements",
//tags="@DirectoryTabNavigationFromDashboardTab",
//tags="@DirectoryTabIsSearchButtonDisplayed",
tags="@LoginValidCredentials or @DashboardTabCountOfQuickLaunhElements or @DirectoryTabNavigationFromDashboardTab or @DirectoryTabIsSearchButtonDisplayed",
//tags="@LoginValidCredentials and not @DashboardTabCountOfQuickLaunhElements and not @DirectoryTabNavigationFromDashboardTab and not @DirectoryTabIsSearchButtonDisplayed",
features ="classpath:features", glue ={"com.sadakar.common","com.sadakar.stepdefinitions",
"com.sadakar.testng.runner"},
plugin ={"pretty","json:target/cucumber-reports/cucumber.json","html:target/cucumber-reports/cucumberreport.html"},
monochrome =true)
publicclassRunCucumberTestextends AbstractTestNGCucumberTests {
}
Step 8: Create testng.xml for the project and configure it in pom.xml
Creating testng.xml is optional when we run the code from Eclipse TestNG
But, what if the code has to be deployed to a Automation server and in it the code has to be triggered.
We use maven command line to trigger the tests.
Create testng.xml file in the project root and give the Runner class name as shown in below.
And, then in the pom.xml configure the testng.xml - go back to to pom.xml and look at the plug-in section.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suitename="Suite">
<testname="Test">
<classes>
<classname="com.sadakar.testng.runner.RunCucumberTest"/>
</classes>
</test>
</suite>
[RemoteTestNG] detected TestNG version 7.0.1
@Dasbhoard@DashboardTabCountOfQuickLaunhElements
Scenario: DashboardTabCountOfQuickLaunhElements # features/Dashboard.feature:9
SLF4J: Failed to load class"org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Starting ChromeDriver 103.0.5060.53 (a1711811edd74ff1cf2150f36ffa3b0dae40b17f-refs/branch-heads/5060@{#853}) on port 50670
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.
Jul 06, 2022 12:49:38 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected upstream dialect: W3C
Jul 06, 2022 12:49:38 PM org.openqa.selenium.devtools.CdpVersionFinder findNearestMatch
INFO: Found exact CDP implementation for version 103
Given User login to HRM application with UserName and Password # com.sadakar.stepdefinitions.HRMLoginPage.loginToHRMApp(io.cucumber.datatable.DataTable)
| Admin | admin123 |
Assign Leave
Leave List
Timesheets
Apply Leave
My Leave
My Timesheet
Size of Quick launch elements : 6
Then User finds the list of quick launch elements # com.sadakar.stepdefinitions.DashboardPage.listOfQuickLaunchElementsOnDashboardPage()
@Dasbhoard @DirectoryTabNavigationFromDashboardTab
Scenario: DirectoryTabNavigationFromDashboardTab # features/Dashboard.feature:13
Starting ChromeDriver 103.0.5060.53 (a1711811edd74ff1cf2150f36ffa3b0dae40b17f-refs/branch-heads/5060@{#853}) on port 52607
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.
Jul 06, 2022 12:49:51 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected upstream dialect: W3C
Jul 06, 2022 12:49:51 PM org.openqa.selenium.devtools.CdpVersionFinder findNearestMatch
INFO: Found exact CDP implementation for version 103
Given User login to HRM application with UserName and Password # com.sadakar.stepdefinitions.HRMLoginPage.loginToHRMApp(io.cucumber.datatable.DataTable)
| Admin | admin123 |
Then User clicks on Directory tab and verifies the navigation # com.sadakar.stepdefinitions.DashboardPage.navigateToDirectoryTabFromDashbaordTab()
@Directory @DirectoryTabIsSearchButtonDisplayed
Scenario: DirectoryTabIsSearchButtonDisplayed # features/Directory.feature:10
Starting ChromeDriver 103.0.5060.53 (a1711811edd74ff1cf2150f36ffa3b0dae40b17f-refs/branch-heads/5060@{#853}) on port 64336
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.
Jul 06, 2022 12:50:11 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected upstream dialect: W3C
Jul 06, 2022 12:50:11 PM org.openqa.selenium.devtools.CdpVersionFinder findNearestMatch
INFO: Found exact CDP implementation for version 103
Given User login to HRM application with UserName and Password # com.sadakar.stepdefinitions.HRMLoginPage.loginToHRMApp(io.cucumber.datatable.DataTable)
| Admin | admin123 |
Then User is on Directory page # com.sadakar.stepdefinitions.DirectoryPage.directoryPage()
Then Is Search button displayed # com.sadakar.stepdefinitions.DirectoryPage.isSearchButtonDisplayed()
@HRMLogin @LoginValidCredentials
Scenario: LoginValidCredentials # features/HRMLogin.feature:6
Starting ChromeDriver 103.0.5060.53 (a1711811edd74ff1cf2150f36ffa3b0dae40b17f-refs/branch-heads/5060@{#853}) on port 56598
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.
Jul 06, 2022 12:50:32 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected upstream dialect: W3C
Jul 06, 2022 12:50:32 PM org.openqa.selenium.devtools.CdpVersionFinder findNearestMatch
INFO: Found exact CDP implementation for version 103
Given User login to HRM application with UserName and Password # com.sadakar.stepdefinitions.HRMLoginPage.loginToHRMApp(io.cucumber.datatable.DataTable)
| Admin | admin123 |
Then User navigates to Dashboard page # com.sadakar.stepdefinitions.HRMLoginPage.navigateToDashboardTab()
????????????????????????????????????????????????????????????????????????????
? View your Cucumber Report at: ?
? https://reports.cucumber.io/reports/bebe12f4-ffa5-46a1-a8e2-11ea5d3ef259 ?
? ?
? This report will self-destruct in 24h. ?
? Keep reports forever: https://reports.cucumber.io/profile ?
????????????????????????????????????????????????????????????????????????????PASSED: runScenario("DashboardTabCountOfQuickLaunhElements", "Optional[Dashboard page]")
Runs Cucumber Scenarios
PASSED: runScenario("DirectoryTabNavigationFromDashboardTab", "Optional[Dashboard page]")
Runs Cucumber Scenarios
PASSED: runScenario("DirectoryTabIsSearchButtonDisplayed", "Optional[Dashboard page]")
Runs Cucumber Scenarios
PASSED: runScenario("LoginValidCredentials", "Optional[Login to HRM Application]")
Runs Cucumber Scenarios
===============================================
Default test
Tests run:4,Failures:0,Skips:0
===============================================
===============================================
Default suite
Total tests run:4,Passes:4,Failures:0,Skips:0
===============================================
Step 10: Run Tests from testng.xml
Logs generated through running testng.xml are same as above in Eclipse.
Step 11: Run Tests from command line
On the command line navigate to the folder where the project is saved and then issue mvn test command ash shown in below images.