Hi
In this tutorial, we'll learn about how to integrate Cucumber with TestNG and Selenium.
Test Strategy:
Verify the OrangeHRM login page with valid credentials
Verify the count of elements in Quick Launch panel in the Dashboard Tab/Page.
Verify the navigation to Directory Tab/Page from Dashboard Tab/Page
Verify the existence of Search button in the Directory Tab/Page
No Voice Video Tutorial (Walkthrough
OR
Clone the project from Github
After login validation is done driver is not quit so this session will be used by any other scenarios that runs randomly and after executing all the scenarios the driver quit from AfterAll hook.
Steps to create the frame work:
Gherikn script for login validation
Dashboard.feature
We are already in logged in into the application in BeforeAll hook - so how to validate login ?
When the driver begins executing this scenario, we quit the driver (driver.quit()) first and then a initiatate a new driver and then logging back to the application that means the new driver is alive.
Integration Approach:
In this frame work, if we want to validate login page as well as other scenarios,
we create one driver session for login validation and another driver session for remaining scenarios.
That is, browser opens for twice - once for login validation and second time for remaining scenarios validation.
Execution of scenarios are independent of each other.
We use BeforeAll hook of Cucumber framework to login into the application so all the scenarios except login scenario shares this driver session from it.
As we are including the validation of logging we quit the session of driver from BeforeAll since we are already logged into the app before all the scenarios run so login validation is not possible.
In the login step definitions we quit the session of the driver from hook and
initiate new driver session so login validation will be done
We use BeforeAll hook of Cucumber framework to login into the application so all the scenarios except login scenario shares this driver session from it.
As we are including the validation of logging we quit the session of driver from BeforeAll since we are already logged into the app before all the scenarios run so login validation is not possible.
In the login step definitions we quit the session of the driver from hook and
initiate new driver session so login validation will be done
After login validation is done driver is not quit so this session will be used by any other scenarios that runs randomly and after executing all the scenarios the driver quit from AfterAll hook.
DISCLAIMER :
This is an experiment on how to avoid login for each scenario from various feature files as we normally use Background band in feature files for login purposes.
Adoption of this approach is one's own interest.
This is an experiment on how to avoid login for each scenario from various feature files as we normally use Background band in feature files for login purposes.
Adoption of this approach is one's own interest.
Framework set-up consists of :
1. Java 16 (Eclipse Build Path)
2. Cucumber Java 7.4.0 (pom.xml)
3. Cucumber TestNG 7.4.0 (pom.xml)
4. Selenium 4.3.0 (pom.xml)
5. Maven 3.8.1 (Embedded in Eclipse/Installed from Market Place)
(To run the tests from command line install Apache Maven in Windows 10/11 - I've installed 3.8.6)
Steps to create the frame work:
1. Download and install Cucumber plug-in in Eclipse from Market Place
2. Download and install TestNG plug-in in Eclipse from Market Place
3. Create a new Maven project (say : CucumberSeleniumTestNG)
2. Download and install TestNG plug-in in Eclipse from Market Place
3. Create a new Maven project (say : CucumberSeleniumTestNG)
4. Add cucumber-java, cucumber-testng and selenium dependencies in the pom.xml
5. Add Maven compiler dependency in pom.xml
6. Create feature files(Gherkhin script) in src/main/resources folder
6. Create feature files(Gherkhin script) in src/main/resources folder
7. Create BasePage class for driver initialization and
create Hooks class for cucumber
@BeforeAll and
@BeforeAll and
@AfterAll
that runs before all scenarios or after all the scenarios
(Note that these are NOT TestNG annotations)
that runs before all scenarios or after all the scenarios
(Note that these are NOT TestNG annotations)
8. Write Step Definition Or Glue Code for the feature files
9. Create TestNG Cucumber Runner class, CucumberRunner.java
11. Create testng.xml for the project
12. Run Tests from TestNG Tests
13. Run Tests from testng.xml
14. Run Tests from Command line
15. Test results analysis from Cucumber report
16. Test results analysis from TestNG report
16. Test results analysis from TestNG report
Project structure:
1. Download and install Cucumber plug-in in Eclipse from Market Place
2. Download and install TestNG plug-in in Eclipse from Market Place
3. Create a new Maven project (say : CucumberSeleniumTestNG)
2. Download and install TestNG plug-in in Eclipse from Market Place
3. Create a new Maven project (say : CucumberSeleniumTestNG)
4. Add cucumber-java, cucumber-testng and selenium dependencies in the pom.xml
5. Add Maven compiler dependency in pom.xml
pom.xml
Add the cucumber, testng and selenium dependencies and maven compiler as starting point.
Add the cucumber, testng and selenium dependencies and maven compiler as starting point.
<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>CucumberSeleniumTestNG</groupId>
<artifactId>CucumberSeleniumTestNG</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.4.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.4.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>
</dependencies>
<build>
<pluginManagement>
<plugins>
<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>
6. Create feature files(Gherkhin script) in src/main/resources folder
HRMLogin.featureGherikn script for login validation
@HRMLogin
Feature: Login to HRM Application
I want to use this template for HRM Login page
@LoginValidCredentials
Scenario: LoginValidCredentials
Given User is on login page
When User enters username as "Admin" and password as "admin123"
Then User should be able to login successfully
Dashboard.feature
There are two sceanrios in this feature file
Directory.feature
There is a scenario with Background added to the feature
BasePage.java
In the BasePage we initiate the driver; if we want to do command line execution of scenarios from a generated jar, main() method has to be included and this class best suites for it.
Hooks.java
In the BeforeAll (This is a cucumber annotation/hook) hook we are logging into the application and use this driver session for all the scenarios so multiple log-in for each scenario is avoidable except for login validation.
@Dasbhoard
Feature: Dashboard page
I want to use this template for my Dashboard Page
@DashboardTabCountOfQuickLaunhElements
Scenario: DashboardTabCountOfQuickLaunhElements
Then User finds the list of quick launch elements@DirectoryTabNavigationFromDashbaordTab
Scenario: DirectoryTabNavigationFromDashboardTab Then User clicks on Directory tab and verifies the navigation
There is a scenario with Background added to the feature
@Directory
Feature: Dashboard page
I want to use this template for my Directory Page
Background:
Then User is on Directory page
@DirectoryTabIsSearchButtonDisplayed
Scenario: DirectoryTabIsSearchButtonDisplayed
Then Is Search button displayed
7. Create BasePage class for driver initialization and
create Hooks class for cucumber
@BeforeAll and
@BeforeAll and
@AfterAll
that runs before all scenarios or after all the scenarios
(Note that these are NOT TestNG annotations)
that runs before all scenarios or after all the scenarios
(Note that these are NOT TestNG annotations)
In the BasePage we initiate the driver; if we want to do command line execution of scenarios from a generated jar, main() method has to be included and this class best suites for it.
package com.sadakar.common;
importorg.openqa.selenium.WebDriver;
publicclassBasePage{
publicstatic WebDriver driver;
}
In the BeforeAll (This is a cucumber annotation/hook) hook we are logging into the application and use this driver session for all the scenarios so multiple log-in for each scenario is avoidable except for login validation.
quit the driver after executing all the scenarios is done using AfterAll hook (This is a cucumber annotation/hook)
package com.sadakar.common;
importjava.time.Duration;
importorg.openqa.selenium.By;
importorg.openqa.selenium.chrome.ChromeDriver;
importio.cucumber.java.AfterAll;
importio.cucumber.java.BeforeAll;
publicclassHooksextends BasePage {
@BeforeAll
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");
driver.findElement(By.xpath("//*[@id=\"txtUsername\"]")).sendKeys("Admin");
driver.findElement(By.xpath("//*[@id=\"txtPassword\"]")).sendKeys("admin123");
driver.findElement(By.id("btnLogin")).submit();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
}
@AfterAll
publicstaticvoidquitDriver()throws Exception {
driver.quit();
}
}
8. Write Step Definition Or Glue Code for the feature files
HRMLoginPage.javaWe are already in logged in into the application in BeforeAll hook - so how to validate login ?
When the driver begins executing this scenario, we quit the driver (driver.quit()) first and then a initiatate a new driver and then logging back to the application that means the new driver is alive.
As the cucumber scenarios executes randomly, if there are any other scenarios to be executed those scenarios will use the secondly generated driver from the login done page.
DashboardPage.java
There are two scenarios in Dashboard.feature file , one scenario is to count the elements and other scenario is to navigate from Dashboard to Dictionary.
In this class, we have the step definition code for those two scenarios.
DirectoryPage.java
In the Directory feature we are validation whether the Search button is displayed or not.
package com.sadakar.stepdefinitions;
importjava.time.Duration;
importorg.openqa.selenium.By;
importorg.openqa.selenium.chrome.ChromeDriver;
importcom.sadakar.common.BasePage;
importio.cucumber.java.en.Given;
importio.cucumber.java.en.Then;
importio.cucumber.java.en.When;
publicclassHRMLoginPageextends BasePage {
@Given("User is on login page")
publicstaticvoidhomePage()throws InterruptedException {
driver.quit();
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");
}
@When("User enters username as {string} and password as {string}")
publicvoidenterUserNamePassword(String userName, String password){
driver.findElement(By.xpath("//*[@id=\"txtUsername\"]")).sendKeys(userName);
driver.findElement(By.xpath("//*[@id=\"txtPassword\"]")).sendKeys(password);
}
@Then("User should be able to login successfully")
publicvoidclickSubmitLogin(){
driver.findElement(By.id("btnLogin")).submit();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
}
}
There are two scenarios in Dashboard.feature file , one scenario is to count the elements and other scenario is to navigate from Dashboard to Dictionary.
In this class, we have the step definition code for those two scenarios.
package com.sadakar.stepdefinitions;
importjava.time.Duration;
importjava.util.ArrayList;
importjava.util.List;
importorg.openqa.selenium.By;
importorg.openqa.selenium.WebElement;
importorg.testng.Assert;
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());
// Adding WebElements List to a ArrayList
List<String> quickLaunchElementsArrayList =new ArrayList<String>();
for(WebElement ele : actualListOfQuickLaunchElements){
quickLaunchElementsArrayList.add(ele.getText());
}
// Displaying the WebElements from the ArrayList
for(WebElement s : actualListOfQuickLaunchElements){
System.out.println(s.getText());
}
// Size of the ArrayList
System.out.println("Size of Array list : "+ quickLaunchElementsArrayList.size());
// Preparing expected list of elements
@SuppressWarnings("serial")
List<String> expecteListOfQuickLaunchElements =new ArrayList<String>(){
{
add("Assign Leave");
add("Leave List");
add("Timesheets");
add("Apply Leave");
add("My Leave");
add("My Timesheet");
}
};
// comparing actual list with expected list
for(int i =0; i < actualListOfQuickLaunchElements.size(); i++){
String actualLabels = actualListOfQuickLaunchElements.get(i).getText();
String expectedLabels = expecteListOfQuickLaunchElements.get(i);
Assert.assertEquals(actualLabels, expectedLabels);
}
}
@Then("User clicks on Directory tab and verifies the navigation")
publicvoidnavigateToDirectoryTabFromDashbaordTab(){
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(20));
driver.findElement(By.xpath("//*[@id=\"menu_directory_viewDirectory\"]")).click();
}
}
In the Directory feature we are validation whether the Search button is displayed or not.
package com.sadakar.stepdefinitions;
importorg.openqa.selenium.By;
importcom.sadakar.common.BasePage;
importio.cucumber.java.en.Then;
publicclassDirectoryPageextends BasePage{
@Then("User is on Directory page")
publicvoiduser_is_on_directory_page(){
driver.findElement(By.xpath("//*[@id=\"menu_directory_viewDirectory\"]/b")).click();
}
@Then("Is Search button displayed")
publicvoidisSearchButtonDisplayed(){
driver.findElement(By.xpath("//*[@id=\"searchBtn\"]")).isDisplayed();
}
}
9. Create TestNG Cucumber Runner class, CucumberRunner.java
CucumberRunner.javaIn the cucumber 7 the tags takes new format
For example:
Single tag :
tags="@LoginValidCredentials",
tags="@LoginValidCredentials",
Two tags :
tags="@LoginValidCredentials or @DashboardTabCountOfQuickLaunhElements",
tags="@LoginValidCredentials or @DashboardTabCountOfQuickLaunhElements",
Negate a tag :
tags="@LoginValidCredentials and not @DashboardTabCountOfQuickLaunhElements",
tags="@LoginValidCredentials and not @DashboardTabCountOfQuickLaunhElements",
CucumberRunner class extends the AbstractTestNGCucumberTests
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 or @DirectoryTabIsSearchButtonDisplayed",
features ="classpath:cucumberfeatures", glue ={"com.sadakar.common","com.sadakar.stepdefinitions",
"com.sadakar.testng.runner","com.inovalon.cucumber.common"},
plugin ={"pretty","json:target/cucumber-reports/cucumber.json","html:target/cucumber-reports/cucumberreport.html"},
monochrome =true)
publicclassCucumberRunnerextends AbstractTestNGCucumberTests {
}
11. Create testng.xml for the project
testng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suitename="Suite">
<testthread-count="5"name="Test">
<classes>
<classname="com.sadakar.testng.runner.CucumberRunner"/>
</classes>
</test><!-- Test -->
</suite><!-- Suite -->
Click on the images to Zoom In
Emailable-report.html:
I hope you find it useful ! Stay tuned for more learnings.