The previous tutorial explained generation of Allure Report with Cucumber5, Selenium and TestNG in a Maven project. In this tutorial, I will explain the steps to create an Allure Report with Cucumber, Selenium and TestNG in a Gradle project.
Pre Requisite:
Java 8 or above installed
Eclipse or IntelliJ IDE installed
Gradle Installed
Environment variables JAVA_HOME and GRADLE_HOME correctly configured
In this tutorial, I’ll create a BDD Framework for the testing of web applications using Cucumber, Selenium WebDriver with TestNG. This framework consists of:-
Cucumber Java- 7.6.0
Cucumber TestNG – 7.6.0
Java 11
TestNG – 7.6.0
Gradle – 7.5.1
Selenium – 4.3.0
AspectJ Weaver – 1.9.7
Project Structure
Implementation Steps
Add Cucumber, Selenium, TestNG, and Allure-TestNG dependencies in build.gradle
Create Locator and Action classes and Step Definition corresponding to the feature file and Test Runner Class
Step 1 – Add Cucumber, Selenium, TestNG, and Allure-TestNG dependencies in build.gradle
/*
* This file was generated by the Gradle 'init' task.
*
*/
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
id 'io.qameta.allure' version '2.11.0'
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
java {
sourceCompatibility = 11
targetCompatibility = 11
}
dependencies {
// Use TestNG framework, also requires calling test.useTestNG() below
testImplementation 'io.cucumber:cucumber-java:7.6.0'
testImplementation 'io.cucumber:cucumber-testng:7.6.0'
// Allure
implementation 'io.qameta.allure:allure-cucumber7-jvm:2.19.0'
runtimeOnly 'org.aspectj:aspectjweaver:1.9.7'
//TestNG
testImplementation 'org.testng:testng:7.6.0'
//Others
implementation 'com.google.guava:guava:31.0.1-jre'
implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
implementation 'io.github.bonigarcia:webdrivermanager:5.3.0'
}
application {
// Define the main class for the application.
mainClass = 'com.example.App'
}
tasks.named('test') {
// Use TestNG for unit tests.
useTestNG()
}
configurations {
cucumberRuntime {
extendsFrom testImplementation
}
}
task cucumber() {
dependsOn assemble, compileTestJava
doLast {
javaexec {
systemProperty("allure.results.directory", "build/allure-results")
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty',
'--glue', 'com.example.definitions', 'src/test/resources']
}
}
}
Step 2 – Create Locator and Action classes and Step Definition corresponding to the feature file and Test Runner Class
As mentioned above, there is another tutorial that explains the project structure as well as the feature file and corresponding Step Definitions, please refer to this tutorial – Gradle Project with Cucumber, Selenium and TestNG.
Step 3 – Execute the Tests
Go to the app project and run the tests, using the below command
gradle cucumber
The output of the test execution is
Step 4 – Generate the Allure Report
Once the test execution is finished, a folder named allure-results will be generated in thebuild folder.
Note:- Make sure that you move to folder app, because build folder is present in app folder.
To generate Allure Report, use the below command
allure serve build/allure-results
This will generate the beautiful Allure Test Report as shown below.
Allure Report Dashboard
The overview page hosts several default widgets representing basic characteristics of your project and test environment.
Statistics – overall report statistics.
Launches – if this report represents several test launches, statistics per launch will be shown here.
Behaviors – information on results aggregated according to stories and features.
Executors – information on test executors that were used to run the tests.
History Trend – if tests accumulated some historical data, its trend will be calculated and shown on the graph.
Environment – information on the test environment.
Categories in Allure Report
The categories tab gives you the way to create custom defects classification to apply for test results. There are two categories of defects – Product Defects (failed tests) and Test Defects (broken tests).
Suites in Allure Report
On the Suites tab a standard structural representation of executed tests, grouped by suites and classes can be found.
Graphs in Allure Report
Graphs allow you to see different statistics collected from the test data: status breakdown or severity and duration diagrams.
Timeline in Allure Report
The timeline tab visualizes retrospective test execution, allure adaptors collect precise timings of tests, and here on this tab, they are arranged accordingly to their sequential or parallel timing structure.
Behaviors of Allure Report
This tab groups test results according to Epic, Feature, Story, Test Severity, Test Description, Test Steps, and so on.
Packages in Allure Report
The packages tab represents a tree-like layout of test results, grouped by different packages.
Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
The previous tutorial explained the Integration of Cucumber with Selenium and TestNG in a Maven Project. This tutorial explains the test automation framework based on Gradle, Cucumber, Selenium, and TestNG.
Pre Requisite:
Java 8 or above installed
Eclipse or IntelliJ IDE installed
Gradle Installed
Environment variables JAVA_HOME and GRADLE_HOME are correctly configured
In this tutorial, I’ll create a BDD Framework for the testing of web applications using Cucumber, and Selenium WebDriver with TestNG. This framework consists of:-
Cucumber Java- 7.6.0
Cucumber TestNG – 7.6.0
Java 11
TestNG – 7.6.0
Gradle – 7.5.1
Selenium – 4.3.0
Project Structure
Steps to set up Cucumber Test Automation Framework with Selenium and TestNG
Download and Install Java on the system
Download and setup Eclipse IDE on the system
Install and setup Gradle
Install Cucumber Eclipse Plugin (For Eclipse IDE)
Create a new Gradle Project
Add Selenium, TestNG, and Cucumberdependencies to the build.gradle
Create a featurefile under src/test/resources
Create the classes for locators, actions and utilitiesin src/main/java
Create the Step Definition class or Glue Code in src/test/java
Create a Hook class to contain the initialization and closing of browser in src/test/java
Create a TestNG Cucumber Runnerclass in src/test/java
Run the tests from Command Line
Cucumber Report Generation
Implementation Steps
Step 1- Download and Install Java
Cucumber and Selenium need Java to be installed on the system to run the tests. Click here to know How to install Java.
Step 2 – Download and setup Eclipse IDE on the system
The Eclipse IDE (integrated development environment) provides strong support for Java developers. Click here to know How to install Eclipse.
Step 3 – Setup Maven
To build a test framework, we need to add a number of dependencies to the project. Click here to know How to install Maven.
Step 4 – Install Cucumber Eclipse Plugin
The cucumber plugin is an Eclipse plugin that allows eclipse to understand the Gherkin syntax. When we are working with cucumber we will write the feature files that contain Feature, Scenario, Given, When, Then, And, But, Tags, Scenario Outline, and Examples. By default, eclipse doesn’t understand these keywords so it doesn’t show any syntax highlighter. Cucumber Eclipse Plugin highlights the keywords present in Feature File. Refer to this tutorial to get more detail – How to setup Cucumber with Eclipse.
Step 5 – Create a new Gradle Project
Below are the steps to create the Gradle project from the command line.
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java application project to get you started.
*/
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
id 'io.qameta.allure' version '2.11.0'
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
java {
sourceCompatibility = 11
targetCompatibility = 11
}
dependencies {
// Use TestNG framework, also requires calling test.useTestNG() below
testImplementation 'io.cucumber:cucumber-java:7.6.0'
testImplementation 'io.cucumber:cucumber-testng:7.6.0'
//TestNG
testImplementation 'org.testng:testng:7.6.0'
//Others
implementation 'com.google.guava:guava:31.0.1-jre'
implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
implementation 'io.github.bonigarcia:webdrivermanager:5.3.0'
}
application {
// Define the main class for the application.
mainClass = 'com.example.App'
}
tasks.named('test') {
// Use TestNG for unit tests.
useTestNG()
}
configurations {
cucumberRuntime {
extendsFrom testImplementation
}
}
task cucumber() {
dependsOn assemble, testClasses
doLast {
javaexec {
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty',
'--glue', 'com.example.definitions', 'src/test/resources']
}
}
}
Step 7 – Create a feature file in the src/test/resources directory
Create a folder with name features. Now, create the feature file in this folder. The feature file should be saved with the extension .feature. This feature file contains the test scenarios created to test the application. The Test Scenarios are written in Gherkins language in the format of Given, When, Then, And, But.
Below is an example of the Feature File.
Feature: Login to HRM Application
Background:
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
@ValidCredentials
Scenario: Login with valid credentials
When User enters username as "Admin" and password as "admin123"
Then User should be able to login sucessfully and new page open
@InvalidCredentials
Scenario Outline: Login with invalid credentials
When User enters username as "<username>" and password as "<password>"
Then User should be able to see error message "<errorMessage>"
Examples:
| username | password | errorMessage |
| Admin | admin12$$ | Invalid credentials |
| admin$$ | admin123 | Invalid credentials |
| abc123 | xyz$$ | Invalid credentials |
@MissingUsername
Scenario: Verify error message when username is missing
When User enters username as "" and password as "admin123"
Then User should be able to see error message "Empty Username"
Step 8 – Create the classes for locators, actions and utilities in src/main/java
Below is the sample code of the LoginPageLocators.
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LoginPageLocators {
@FindBy(name = "username")
public WebElement userName;
@FindBy(name = "password")
public WebElement password;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[3]/button")
public WebElement login;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/div/div[1]/div[1]/p")
public WebElement errorMessage;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[1]/div/span")
public WebElement missingUsernameErrorMessage;
}
Below is the sample code for the HomePageLocators.
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class HomePageLocators {
@FindBy(xpath = "//*[@id='app']/div[1]/div[2]/div[2]/div/div[1]/div[1]/div[1]/h5")
public WebElement homePageUserName;
}
Create the action classes for each web page. These action classes contain all the methods needed by the step definitions. In this case, I have created 2 action classes – LoginPageActions and HomePageActions .
LoginPageActions
import org.openqa.selenium.support.PageFactory;
import com.example.locators.LoginPageLocators;
import com.example.utils.HelperClass;
public class LoginPageActions {
LoginPageLocators loginPageLocators = null;
public LoginPageActions() {
this.loginPageLocators = new LoginPageLocators();
PageFactory.initElements(HelperClass.getDriver(),loginPageLocators);
}
public void login(String strUserName, String strPassword) {
loginPageLocators.userName.sendKeys(strUserName);
loginPageLocators.password.sendKeys(strPassword);
loginPageLocators.login.click();
}
// Get the error message when invalid credentials are provided
public String getErrorMessage() {
return loginPageLocators.errorMessage.getText();
}
// Get the error message when username is blank
public String getMissingUsernameText() {
return loginPageLocators.missingUsernameErrorMessage.getText();
}
}
HomePageActions
import org.openqa.selenium.support.PageFactory;
import com.example.locators.HomePageLocators;
import com.example.utils.HelperClass;
public class HomePageActions {
HomePageLocators homePageLocators = null;
public HomePageActions() {
this.homePageLocators = new HomePageLocators();
PageFactory.initElements(HelperClass.getDriver(),homePageLocators);
}
// Get the User name from Home Page
public String getHomePageText() {
return homePageLocators.homePageUserName.getText();
}
}
Create a Helper class where we are initializing the web driver, initializing the web driver wait, defining the timeouts, and creating a private constructor of the class, it will declare the web driver, so whenever we create an object of this class, a new web browser is invoked.
import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import io.github.bonigarcia.wdm.WebDriverManager;
public class HelperClass {
private static HelperClass helperClass;
private static WebDriver driver;
public final static int TIMEOUT = 10;
private HelperClass() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
driver.manage().window().maximize();
}
public static void openPage(String url) {
driver.get(url);
}
public static WebDriver getDriver() {
return driver;
}
public static void setUpDriver() {
if (helperClass==null) {
helperClass = new HelperClass();
}
}
public static void tearDown() {
if(driver!=null) {
driver.close();
driver.quit();
}
helperClass = null;
}
}
Step 9 – Create the Step Definition class or Glue Code in src/test/java
Now, we need to create the Step Definition of the Feature File – LoginPageDefinitions.java.
import org.testng.Assert;
import org.testng.SkipException;
import com.example.actions.HomePageActions;
import com.example.actions.LoginPageActions;
import com.example.utils.HelperClass;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
public class LoginPageDefinitions {
LoginPageActions objLogin = new LoginPageActions();
HomePageActions objHomePage = new HomePageActions();
@Given("User is on HRMLogin page {string}")
public void loginTest(String url) {
HelperClass.openPage(url);
}
@When("User enters username as {string} and password as {string}")
public void goToHomePage(String userName, String passWord) {
objLogin.login(userName, passWord);
}
@Then("User should be able to login sucessfully and new page open")
public void verifyLogin() {
Assert.assertTrue(objHomePage.getHomePageText().contains("Employee Information"));
}
@Then("User should be able to see error message {string}")
public void verifyErrorMessageForInvalidCredentials(String expectedErrorMessage) {
Assert.assertEquals(objLogin.getErrorMessage(),expectedErrorMessage);
}
@Then("User should be able to see error message for empty username as {string}")
public void verifyErrorMessageForEmptyUsername(String expectedErrorMessage) {
Assert.assertEquals(objLogin.getMissingUsernameText(),expectedErrorMessage);
}
}
Step 10 – Create a Hook class to contain the initialization and closing of the browser in src/test/java
Below is the example of the Hook class where we initialize the browser as well as close the browser at the end of the execution.
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import com.example.utils.HelperClass;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
public class BaseClass {
@Before
public static void setUp() {
HelperClass.setUpDriver();
}
@After
public static void tearDown(Scenario scenario) {
//validate if scenario has failed
if(scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) HelperClass.getDriver()).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", scenario.getName());
}
HelperClass.tearDown();
}
}
Step 11 – Create a TestNG Cucumber Runner class in src/test/java
Cucumber needs a TestRunner class to run the feature files. It is suggested to create a folder with the name of the runner in the src/test/java directory and create the Cucumber TestRunner class in this folder. Below is the code of the Cucumber TestRunner class.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "", features = {"src/test/resources/features/LoginPage.feature"}, glue = {"com.example.definitions"})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
Step 12 – Run the tests from Command Line
Run the below command in the command prompt to run the tests and to get the test execution report.
gradle cucumber
The output of the above program is
Step 13 – 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 the Cucumber Service.
In the above example, as we can see, one of the tests has failed. So, when a test fails, we have written the code to take a screenshot of the failed step. The highlighted box above shows the image of the failed test. You can click on that to see the screenshot.
Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
Add Selenium, TestNG, and Allure-TestNG dependencies in build.gradle
Create Pages and Test Code for the pages
Create testng.xml
Execute the Tests
Generate Allure Report
Step 1 – Add Selenium, TestNG, and Allure-TestNG dependencies in the build.gradle
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
id 'io.qameta.allure' version '2.11.0'
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
java {
sourceCompatibility = 11
targetCompatibility = 11
}
dependencies {
// Use TestNG framework, also requires calling test.useTestNG() below
testImplementation 'org.testng:testng:7.6.1'
// This dependency is used by the application.
implementation 'com.google.guava:guava:31.0.1-jre'
implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
implementation 'io.github.bonigarcia:webdrivermanager:5.3.0'
implementation 'io.qameta.allure:allure-testng:2.19.0'
}
application {
// Define the main class for the application.
mainClass = 'com.example.App'
}
tasks.named('test') {
// Use TestNG for unit tests.
useTestNG() {
useDefaultListeners = true
suites "./testng.xml"
}
testLogging {
events "PASSED", "FAILED", "SKIPPED"
exceptionFormat = 'full'
}
}
Step 2 – Create Pages and Test Code for the pages
Below is the sample project which uses Selenium and TestNG which is used to generate an Allure Report.
We have used the PageFactory model to build the tests. I have created a package named pages and created the page classes in that folder. Page class contains the locators of each web element present on that particular page along with the methods of performing actions using these web elements.
This is the BaseClass that contains the PageFactory.initElements. The initElements is a static method of PageFactory class that is used to initialize all the web elements located by @FindBy annotation.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;
public class BasePage {
public WebDriver driver;
public BasePage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver,this);
}
}
Below is the code for LoginPage and HomePage
LoginPage
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LoginPage extends BasePage{
public LoginPage(WebDriver driver) {
super(driver);
}
@FindBy(name = "username")
public WebElement userName;
@FindBy(name = "password")
public WebElement password;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[1]/div/span")
public WebElement missingUsernameErrorMessage;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[1]/div/span")
public WebElement missingPasswordErrorMessage;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[3]/button")
public WebElement login;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/div/div[1]/div[1]/p")
public WebElement errorMessage;
// Get the error message when password is blank
public String getMissingPasswordText() {
return missingPasswordErrorMessage.getText();
}
// Get the Error Message
public String getErrorMessage() {
return errorMessage.getText();
}
public void login(String strUserName, String strPassword) {
userName.sendKeys(strUserName);
password.sendKeys(strPassword);
login.click();
}
}
HomePage
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class HomePage extends BasePage {
public HomePage(WebDriver driver) {
super(driver);
}
@FindBy(xpath = "//*[@id='app']/div[1]/div[2]/div[2]/div/div[1]/div[1]/div[1]/h5")
public WebElement homePageUserName;
// Get the User name from Home Page
public String getHomePageText() {
return homePageUserName.getText();
}
}
Here, we have BaseTests Class also which contains the common methods needed by other test pages.
import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import io.github.bonigarcia.wdm.WebDriverManager;
import io.qameta.allure.Step;
public class BaseTests {
public WebDriver driver;
public final static int TIMEOUT = 30;
@BeforeMethod
@Step("Start the application")
public void setup() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.get("https://opensource-demo.orangehrmlive.com/");
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
}
@Step("Stop the application")
@AfterMethod
public void tearDown() {
driver.quit();
}
}
LoginTests
import org.testng.Assert;
import org.testng.annotations.Test;
import io.qameta.allure.Description;
import io.qameta.allure.Severity;
import io.qameta.allure.SeverityLevel;
public class LoginTests extends BaseTests{
@Severity(SeverityLevel.NORMAL)
@Test(description = "This test validates error message when credentials are incorrect", priority = 0)
@Description("Test Description : Login Test with invalid credentials")
public void invalidCredentials() {
LoginPage objLoginPage = new LoginPage(driver);
objLoginPage.login("Admin", "admin123$$");
// Verify Error Message
Assert.assertEquals(objLoginPage.getErrorMessage(),"Invalid credentials");
}
@Severity(SeverityLevel.BLOCKER)
@Test(description = "This test validates login to the application", priority = 1)
@Description("Test Description : Login Test with valid credentials")
public void gotoHomePage() {
LoginPage objLoginPage = new LoginPage(driver);
objLoginPage.login("Admin", "admin123");
HomePage objHomePage = new HomePage(driver);
// Verify Home Page
Assert.assertEquals(objHomePage.getHomePageText(),"Employee Information");
}
@Severity(SeverityLevel.NORMAL)
@Test(description = "This test will fail", priority = 2)
@Description("Test Description : Login Test with missing username")
public void missingUsername() {
LoginPage objLoginPage = new LoginPage(driver);
objLoginPage.login("", "admin123");
// Verify Error Message
Assert.assertEquals(objLoginPage.getMissingUsernameText(),"Invalid credentials");
}
@Severity(SeverityLevel.NORMAL)
@Test(description = "This test will skip", priority = 3, enabled = false)
@Description("Test Description : Login Test with missing password")
public void missingPassword() {
LoginPage objLoginPage = new LoginPage(driver);
objLoginPage.login("admin", "");
// Verify Error Message
Assert.assertEquals(objLoginPage.getErrorMessage(),"Invalid credentials");
}
}
Step 3 – Create testng.xml
Right-click on the project and select TestNG -> Convert to TestNG.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Gradle - Selenium with TestNG Tests">
<classes>
<class name="com.example.LoginTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Step 4 – Execute the Tests
Note:- As you can see my project has two parts – the app and GradleSeleniumTestNG.
Go to the app project and run the tests, using the below command
gradle clean test
The output of the test execution is
Step 5 – Generate the Allure Report
Once the test execution is finished, a folder named allure-results will be generated in thebuild folder.
To generate Allure Report, use the below command
allure serve build/allure-results
This will generate the beautiful Allure Test Report as shown below.
Allure Report Dashboard
The overview page hosts several default widgets representing the basic characteristics of your project and test environment.
Statistics – overall report statistics.
Launches – if this report represents several test launches, statistics per launch will be shown here.
Behaviors – information on results aggregated according to stories and features.
Executors – information on test executors that were used to run the tests.
History Trend – if tests accumulated some historical data, its trend will be calculated and shown on the graph.
Environment – information on the test environment.
Categories in Allure Report
The categories tab gives you a way to create custom defects classifications to apply for test results. There are two categories of defects – Product Defects (failed tests) and Test Defects (broken tests).
Suites in Allure Report
On the Suites tab a standard structural representation of executed tests, grouped by suites and classes can be found.
Here, I have used TestNG, so to skip the tests have used enabled. But, in the Allure Report, it is marked as unknown (pink color).
Graphs in Allure Report
Graphs allow you to see different statistics collected from the test data: status breakdown or severity and duration diagrams.
Timeline in Allure Report
The timeline tab visualizes retrospective test execution, allure adaptors collect precise timings of tests, and here on this tab, they are arranged accordingly to their sequential or parallel timing structure.
Behaviors of Allure Report
This tab groups test results according to Epic, Feature, Story, Test Severity, Test Description, Test Steps, and so on.
Packages in Allure Report
The packages tab represents a tree-like layout of test results, grouped by different packages.
Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
In this tutorial, we will generate PDF reports using an Extent Adapter.
Step 1: Followthis article to add POM.xml, create and configure the sample project with the extent property file, and add the plugin to the Test Runner class.
Execute the test code. The PDF report will be generated as shown below:
The report contains six sections – dashboard,summary, tags, features, scenarios, and detailed sections.
1. Dashboard
This section is a single-page dashboard that summarizes the test run. This contains the report title, duration, and status of breakups.
2. Summary section
This section provides an overview of the test run in terms of a feature breakdown, comprising duration, scenario count, and step count. The scenarios and steps are divided into status counts. The feature name has a link that navigates to further details in the detailed step section. This link is only present if the detailed section is enabled.
3. Tag section
This section provides an overview of the test run in terms of a tag breakdown, comprising feature count and scenario count.
4. Feature section
This section describes the feature details with a stacked bar chart and a table of the scenario status and duration. This section display can be controlled by a configuration setting, enabled by default. The feature name has a link that navigates to further details in the detailed step section. This link is only present if the detailed section is enabled.
5. Scenario section
This section describes the scenario details with a stacked bar chart and a table of the step status and duration. This section display can be controlled by a configuration setting, enabled by default. The feature and scenario names have a link that navigates to further details in the detailed step section. This link is only present if the detailed section is enabled.
6. Detailed section
This section describes the details of individual steps and hooks, along with status and duration. This section display can be controlled by a configuration setting, enabled by default.
This section also contains screenshots of the failed images.
Customized PDF Report
The report settings can be used to toggle on and off optional report sections, and change the report title, text color for various data, background color, and other options.
The settings are saved in a YAML file called pdf-config.yaml, which is located in the project’s src/test/resources folder. If the file is missing or no settings are specified, the default values are used. To change the default values, create a pdf-config.yaml file in the project’s src/test/resources folder that contains only the new values for the settings.
Execute the test code. Now the PDF Report will be generated as shown below:
This method of configuring report settings using a yaml properties file can be used both for the Maven plugin report generation and the ExtentReport style.
The passed, failed, and skipped colors can be set with the passColor, failColor and skipColor properties. These take in the colors in hex values (without the leading ‘#’) and are valid throughout the report.
The features, scenarios, and detailed sections can be displayed by setting the displayFeature, displayScenario and displayDetailed properties to true. The default value for these settings is true.
Screenshots are displayed as thumbnails and can be opened in the available native application. This is the default behaviour, in which the screenshot file is embedded in the PDF file. This can be toggled by the displayAttached setting. When the setting is set to false, only the thumbnail is displayed. These can also be displayed in zoomed images in a separate section by setting the displayExpanded to true and also displayAttached to false.
To know more about various settings in PDF Report, refer to this tutorial.
ExtentReport is a logger-style reporting library for automated tests. ExtentReports uses the logging style to add information about test sessions, such as the creation of tests, adding screenshots, assigning tags, and adding events or series of steps to sequentially indicate the flow of test steps. ExtentReports 5 is built on an open-Core. That means, both community and professional editions use the same, full-featured API with the exception of a few reporters.
Extent Report 4 onwards, there are 2 editions of Extent Report – Core and Professional.
Below is the screenshot that shows which reporters are available in Professional or Community Editions. You can also visit this page.
This tutorial explains the use of Extent Report Core Edition.
Step 2: Create a feature file in src/test/resources
Below is a sample feature file. I have also added a failed scenario in @FaceBookLink.
Feature: Login to HRM Application
@ValidCredentials
Scenario: Login with valid credentials
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
When User enters username as "Admin" and password as "admin123"
Then User should be able to login sucessfully and new page open
@InvalidCredentials
Scenario Outline: Login with invalid credentials
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
When User enters username as "<username>" and password as "<password>"
Then User should be able to see error message "<errorMessage>"
Examples:
| username | password | errorMessage |
| | abc | Username cannot be empty |
| admin | | Password cannot be empty |
| | | Username cannot be empty |
| Admin | admin12$$ | Invalid credentials |
| admin$$ | admin123 | Invalid credentials |
@FaceBookLink
Scenario: Verify FaceBook Icon on Login Page
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
Then User should be able to see FaceBook Icon
@LinkedInLink
Scenario: Verify LinkedIn Icon on Login Page
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
Then User should be able to see LinkedIn Icon
Step 3: Create extent.properties file in src/test/resources
We need to create the extent.properties file at the src/test/resources folder for the grasshopper extent report adapter to recognize it. Using a property file for reporting is quite helpful if you want to define several different properties.
Let’s enable spark report in an extent properties file:
We have used Page Object Model with Cucumber and TestNG.
Create a Helperclass where we are initializing the web driver, initializing the web driver wait, defining the timeouts, and creating a private constructor of the class, within it will declare the web driver, so whenever we create an object of this class, a new web browser is invoked. We are using a setter and getter method to get the object of Chromedriver with the help of a private constructor itself within the same class.
HelperClass
import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import io.github.bonigarcia.wdm.WebDriverManager;
public class HelperClass {
private static HelperClass helperClass;
private static WebDriver driver;
private static WebDriverWait wait;
public final static int TIMEOUT = 10;
private HelperClass() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT));
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
driver.manage().window().maximize();
}
public static void openPage(String url) {
driver.get(url);
}
public static WebDriver getDriver() {
return driver;
}
public static void setUpDriver() {
if (helperClass==null) {
helperClass = new HelperClass();
}
}
public static void tearDown() {
if(driver!=null) {
driver.close();
driver.quit();
}
helperClass = null;
}
}
Step 5: Create Locator classes in src/main/java
Create a locator class for each page that contains the detail of the locators of all the web elements. Here, I’m creating 2 locator classes – LoginPageLocators and HomePageLocators.
LoginPageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LoginPageLocators {
@FindBy(name = "txtUsername")
public WebElement userName;
@FindBy(name = "txtPassword")
public WebElement password;
@FindBy(id = "logInPanelHeading")
public WebElement titleText;
@FindBy(id = "btnLogin")
public WebElement login;
@FindBy(id = "spanMessage")
public WebElement errorMessage;
@FindBy(xpath = "//*[@id='social-icons']/a[1]/img")
public WebElement linkedInIcon;
@FindBy(xpath = "//*[@id='social-icons']/a[6]/img") //Invalid Xpath
public WebElement faceBookIcon;
}
HomePageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class HomePageLocators {
@FindBy(id = "welcome")
public WebElement homePageUserName;
}
Step 6: Create Action classes in src/main/java
Create the action classes for each web page. These action classes contain all the methods needed by the step definitions. In this case, I have created 2 action classes – LoginPageActions and HomePageActions
LoginPageActions
In this class, the very first thing will do is to create the object of LoginPageLocators class so that we should be able to access all the PageFactory elements. Secondly, create a public constructor of LoginPageActions class
import org.openqa.selenium.support.PageFactory;
import com.example.locators.LoginPageLocators;
import com.example.utils.HelperClass;
public class LoginPageActions {
LoginPageLocators loginPageLocators = null;
public LoginPageActions() {
this.loginPageLocators = new LoginPageLocators();
PageFactory.initElements(HelperClass.getDriver(),loginPageLocators);
}
// Set user name in textbox
public void setUserName(String strUserName) {
loginPageLocators.userName.sendKeys(strUserName);
}
// Set password in password textbox
public void setPassword(String strPassword) {
loginPageLocators.password.sendKeys(strPassword);
}
// Click on login button
public void clickLogin() {
loginPageLocators.login.click();
}
// Get the title of Login Page
public String getLoginTitle() {
return loginPageLocators.titleText.getText();
}
// Get the title of Login Page
public String getErrorMessage() {
return loginPageLocators.errorMessage.getText();
}
// LinkedIn Icon is displayed
public Boolean getLinkedInIcon() {
return loginPageLocators.linkedInIcon.isDisplayed();
}
// FaceBook Icon is displayed
public Boolean getFaceBookIcon() {
return loginPageLocators.faceBookIcon.isDisplayed();
}
public void login(String strUserName, String strPassword) {
// Fill user name
this.setUserName(strUserName);
// Fill password
this.setPassword(strPassword);
// Click Login button
this.clickLogin();
}
}
HomePageActions
import org.openqa.selenium.support.PageFactory;
import com.example.locators.HomePageLocators;
import com.example.utils.HelperClass;
public class HomePageActions {
HomePageLocators homePageLocators = null;
public HomePageActions() {
this.homePageLocators = new HomePageLocators();
PageFactory.initElements(HelperClass.getDriver(),homePageLocators);
}
// Get the User name from Home Page
public String getHomePageText() {
return homePageLocators.homePageUserName.getText();
}
}
Step 7: Create a Step Definition file in src/test/java
Create the corresponding Step Definition file of the feature file.
LoginPageDefinitions
import org.testng.Assert;
import com.example.actions.HomePageActions;
import com.example.actions.LoginPageActions;
import com.example.utils.HelperClass;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
public class LoginPageDefinitions{
LoginPageActions objLogin = new LoginPageActions();
HomePageActions objHomePage = new HomePageActions();
@Given("User is on HRMLogin page {string}")
public void loginTest(String url) {
HelperClass.openPage(url);
}
@When("User enters username as {string} and password as {string}")
public void goToHomePage(String userName, String passWord) {
// login to application
objLogin.login(userName, passWord);
// go the next page
}
@Then("User should be able to login sucessfully and new page open")
public void verifyLogin() {
// Verify home page
Assert.assertTrue(objHomePage.getHomePageText().contains("Welcome"));
}
@Then("User should be able to see error message {string}")
public void verifyErrorMessage(String expectedErrorMessage) {
// Verify home page
Assert.assertEquals(objLogin.getErrorMessage(),expectedErrorMessage);
}
@Then("User should be able to see LinkedIn Icon")
public void verifyLinkedInIcon( ) {
Assert.assertTrue(objLogin.getLinkedInIcon());
}
@Then("User should be able to see FaceBook Icon")
public void verifyFaceBookIcon( ) {
Assert.assertTrue(objLogin.getFaceBookIcon());
}
}
Step 8: Create Hook class in src/test/java
Create thehookclass that contains the Before and After hook. @Before hook contains the method to call the setup driver which will initialize the chrome driver. This will be run before any test.
After Hook – Here will call the tearDown method.
import com.example.utils.HelperClass;
import io.cucumber.java.After;
import io.cucumber.java.Before;
public class Hooks {
@Before
public static void setUp() {
HelperClass.setUpDriver();
}
@After
public static void tearDown() {
HelperClass.tearDown();
}
}
Step 9: Create a Cucumber Test Runner class in src/test/java
Add the extent report cucumber adapter to the runner class’s CucumberOption annotation. It is an important component of the configuration. It also ensures that the cucumber runner class recognizes and launches the extent report adapter for cucumber. Please add the following text as a plugin to the CucumberOptions as described below.
This is how your runner class should look after being added to our project. Moreover, be sure to keep the colon “:” at the end.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "", features = "src/test/resources/features/LoginPage.feature", glue = "com.example.definitions",
plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
Step 10: Create the testng.xml for the project
Right-click on the project and select TestNG -> convert to TestNG.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="ExtentReport5 for Cucumber">
<classes>
<class name = "com.example.runner.CucumberRunnerTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Step 11: Execute the code
Right-Click on the Runner class and select Run As -> TestNG Test.
Below is the screenshot of Console. As expected, 7 tests, out of 8 are passed and 1 is failed.
Step 12: View the ExtentReport
Refresh the project and will see a new folder – Report. The ExtentReport will be present in that folder with the name Spark.html.
Right-click and open with Web Browser.
The report also has a summary section that displays the summary of the execution. The summary includes the overview of the pass/fail using a pictogram, start time, end time, and pass/fail details of features as shown in the image below.
Click on the first icon present on the left side of the report. To view the details about the steps, click on the scenarios. Clicking on the scenario will expand, showing off the details of the steps of each scenario.
Step 13: How to customize the report folder name
We learned how to generate an ExtentReport in Cucumber Junit in the previous section. The problem with the previous approach is that it will continue to override the previous report once the new report is created. Typically, we must keep a backup of all the reports generated by previous tests. To accomplish this, we must save each report with a unique report name or folder name.
It’s simple to create reports with different folder names using the Extent reporter plugin adapter. Two settings must be added to our extent. basefolder.name and basefolder.datetimepattern are properties files. The values assigned to these will be combined to form a folder name. As a result, a report will be generated within that. The basefolder.datetimepattern value must be in a valid date-time format.
The value for basefolder.name in the preceding snippet is “Report/SparkReport.” It means that the folder will be named SparkReport, and that it will create a Report folder within the project directory. You can specify the location of your folder. In the following setting, we’ve used a date and time stamp to create unique folder names by concatenating them with the report name.
So, when we run the report, it will generate at the location shown in the image below:
Congratulation!! We are able to create an Extent Report for Cucumber. Happy Learning!!!
ExtentReports is a logger-style reporting library for automated tests. ExtentReports is a library that can be used to build a customized detailed report. It can be integrated with TestNG, JUnit, etc. This report can be built in JAVA, .NET and it provides a detailed summary of each test case and each test step too in a graphical manner. Extent reports produce HTML-based documents that offer several advantages like pie charts, graphs, screenshots addition, and test summary. ExtentReports 4 is built on an open-Core.
In this class, we created a createInstance()method. Also, you need to set your ExtentReports report HTML file location.
import java.io.File;
import java.io.IOException;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.TakesScreenshot;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.Theme;
import com.example.testcases.BaseTests;
import org.openqa.selenium.OutputType;
public class ExtentManager extends BaseTests{
private static ExtentReports extent;
public static String screenshotName;
public static ExtentReports createInstance(String fileName) {
ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);
htmlReporter.config().setTheme(Theme.DARK);
htmlReporter.config().setDocumentTitle(fileName);
htmlReporter.config().setEncoding("utf-8");
htmlReporter.config().setReportName(fileName);
extent = new ExtentReports();
extent.attachReporter(htmlReporter);
extent.setSystemInfo("Release No", "22");
extent.setSystemInfo("Environment", "QA");
extent.setSystemInfo("Build no", "B-12673");
return extent;
}
public static void captureScreenshot() {
TakesScreenshot screenshot = (TakesScreenshot)driver;
// Call method to capture screenshot
File src = screenshot.getScreenshotAs(OutputType.FILE);
try
{
Date d = new Date();
screenshotName = d.toString().replace(":", "_").replace(" ", "_") + ".jpg";
FileUtils.copyFile(src,new File(System.getProperty("user.dir") + "\\reports\\" + screenshotName));
System.out.println("Successfully captured a screenshot");
} catch (IOException e) {
System.out.println("Exception while taking screenshot " + e.getMessage());
}
}
}
The ExtentHtmlReporter is used for creating an HTML file, and it accepts a file path as a parameter.
ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);
The file path represents the path in which our extent report would be generated. This is defined in ExtentListeners class.
static Date d = new Date();
static String fileName = "ExtentReport_" + d.toString().replace(":", "_").replace(" ", "_") + ".html";
private static ExtentReports extent = ExtentManager.createInstance(System.getProperty("user.dir")+"\\reports\\"+fileName);
ExtentHtmlReporter is also used to customize the extent reports. It allows many configurations to be made through the config() method. Some of the configurations that can be made are described below.
We have two themes – STANDARD and DARK for customizing the look and feel of our extent reports.
htmlReporter.config().setTheme(Theme.DARK);
STANDARD Look
htmlReporter.config().setTheme(Theme.STANDARD);
captureScreenshot() is a method in ExtentTest class that attaches the captured screenshot in the Extent Report. It takes the image path where the screenshot has been captured as the parameter and attaches the screenshot to the Extent Report in Selenium.
public static void captureScreenshot() {
TakesScreenshot screenshot = (TakesScreenshot)driver;
// Call method to capture screenshot
File src = screenshot.getScreenshotAs(OutputType.FILE);
try
{
Date d = new Date();
screenshotName = d.toString().replace(":", "_").replace(" ", "_") + ".jpg";
FileUtils.copyFile(src,new File(System.getProperty("user.dir") + "\\reports\\" + screenshotName));
System.out.println("Successfully captured a screenshot");
} catch (IOException e) {
System.out.println("Exception while taking screenshot " + e.getMessage());
}
}
3. Create ExtentListeners class
This class contains the action done by extent report on each step. In our tests, we implement ITestListener and use its methods. TestNG provides the @Listeners annotation, which listens to every event that occurs in a Selenium code. TestNG Listeners are activated either before the test or after the test case. It is an interface that modifies the TestNG behavior. If any event matches an event for which we want the listener to listen then it executes the code, which ultimately results in modifying the default behavior of TestNG. To know more about ITestListener, please refer to this tutorial.
I have defined actions for onTestStart(), onTestSuccess(), onTestFailure(), onTestSkipped() and onFinish() methods.
The ITestListener is an interface that has unimplemented methods by default and we can add lines of code within each method. So whenever a specific event occurs, the code written within that method will be executed.
onTestFailure() is a method in which this listener will be invoked whenever the test fails. Within this method, we shall add our code to capture screenshots whenever the test case fails on execution. The screenshot of the failed test case is also embedded in the report.
onTestSuccess() is a method that is invoked once the test execution is complete and the test has been passed. We shall add the log included in the Extent Report to mark the test case as passed within this method
This class contains the methods to initialize the browser and exit the browser after every test.
import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import io.github.bonigarcia.wdm.WebDriverManager;
public class BaseTests {
public static WebDriver driver;
public WebDriverWait wait;
@BeforeTest
public void setup() throws Exception {
driver = WebDriverManager.firefoxdriver().create();
driver.get("https://opensource-demo.orangehrmlive.com/");
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
driver.manage().window().maximize();
}
@AfterTest
public void closeBrowser() {
driver.close();
}
5. Create the LoginPage class
This class contains the locator of all the web elements and methods needed for the testing of the page.
package com.example.testcases;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class LoginPage extends BaseTests{
WebDriver driver;
@FindBy(name = "txtUsername")
WebElement userName;
@FindBy(name = "txtPassword")
WebElement password;
@FindBy(id = "logInPanelHeading")
WebElement titleText;
@FindBy(id = "btnLogin")
WebElement login;
@FindBy(id="spanMessage")
WebElement errorMessage;
@FindBy(id="forgotPasswordLink")
WebElement forgetPasswordLink;
@FindBy(xpath="//*[@id='social-icons']/a[1]/img")
WebElement linkedInIcon;
public LoginPage(WebDriver driver) {
this.driver = driver;
// This initElements method will create all WebElements
PageFactory.initElements(driver, this);
}
// Set user name in textbox
public void setUserName(String strUserName) {
userName.sendKeys(strUserName);
}
// Set password in password textbox
public void setPassword(String strPassword) {
password.sendKeys(strPassword);
}
// Click on login button
public void clickLogin() {
login.click();
}
// Get the title of Login Page
public String getLoginTitle() {
return titleText.getText();
}
// Get the text of forgotPasswordLink
public String getforgotPasswordLinkText() {
return forgetPasswordLink.getText();
}
// Get the errorMessage
public String getErrorMessage() {
return errorMessage.getText();
}
// Verify linkedInIcon is enabled
public Boolean isEnabledLinkedIn() {
return linkedInIcon.isEnabled();
}
public void login(String strUserName, String strPasword) {
// Fill user name
this.setUserName(strUserName);
// Fill password
this.setPassword(strPasword);
// Click Login button
this.clickLogin();
}
}
6. Create the LoginTests class
This class contains all the tests. As we are using, I have assigned priority to all the tests to run the tests in a specified order.
import static org.testng.Assert.assertTrue;
import org.testng.Assert;
import org.testng.SkipException;
import org.testng.annotations.Test;
public class LoginTests extends BaseTests{
LoginPage objLogin;
@Test(priority = 0)
public void verifyLoginPageTitle() {
// Create Login Page object
objLogin = new LoginPage(driver);
// Verify login page text
String loginPageTitle = objLogin.getLoginTitle();
Assert.assertTrue(loginPageTitle.contains("LOGIN Panel"));
}
@Test(priority = 1)
public void verifyforgetPasswordLink() {
String expectedText= objLogin.getforgotPasswordLinkText();
Assert.assertTrue(expectedText.contains("Forgot your password?"));
}
@Test(priority = 2)
public void HomeTest() {
// login to application
objLogin.login("Admin1", "admin1234");
String expectedError = objLogin.getErrorMessage();
// Verify home page
Assert.assertTrue(expectedError.contains("Username cannot be empty"));
}
@Test(priority = 3)
public void verifyLinkedIn() {
System.out.println("Actual linkedIn Text :" + objLogin.isEnabledLinkedIn());
assertTrue(objLogin.isEnabledLinkedIn());
System.out.println("Im in skip exception");
throw new SkipException("Skipping this exception");
}
}
In this example, there are 4 tests, and out of 4, 2 should pass, 1 should fail and 1 should skip.
7. Create TestNG.xml
In the TestNG.xml file, we shall add our classes and also the listener class.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Extent Report Demo">
<listeners>
<listener class-name ="com.example.extentlisteners.ExtentListeners"/>
</listeners>
<test name="Login Tests">
<classes>
<class name="com.example.testcases.LoginTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
8. Execute the tests from testng.xml
Right-click on testng.xml and select Run As -> TestNG Suite.
9 Test Execution Result
The test execution result can be seen in the console.
10. Extent Report Generation
Refresh the project and will see a folder with the name of the reports present in the project.
Right-click the report and open it in your choice of browser.
Upon opening the Extent Report, you can see the summary of the tests executed.
This is the view of the dashboard in the Extent Report. This page provides a complete view of the total number of tests executed, passed tests, failed tests, the total time taken for executing the tests, and also the classification of the tests based on the category.
Extent reports produce simple and visually appealing reports. Furthermore, the HTML-based report is simple to share with other stakeholders. Extent Reports provide greater detail, allowing testers to be more effective when it comes to quickly debugging software.
Congratulations!! We are able to generate an ExtentReport. Happy Learning!!
The previous tutorial explained running the tests in Selenium4 Grid using the Standalone option. One of the major advantages of Selenium Grid is the ability to run tests parallelly on multiple browsers simultaneously. Parallel testing helps to reduce execution time and efforts and results in a faster time to delivery.
In this tutorial, we will run the same set of tests on Chrome, Firefox, and Edge browsers sequentially initially to confirm that we can perform Cross Browser testing also.
It is very important to construct our tests thread-safe in order to run them in parallel without a problem.
It is recommended to download the exe of various browsers in the same location where the Selenium WebDriver jar file is present. This is because Selenium 4 Alpha has the ability to automatically detect the WebDrivers present on the node machine. For this example, I have downloaded Chrome, Firefox, and edge drivers.
3. Start Selenium Server Jar
Open a command line terminal. Go to the location where these files are present
cd C:/Users/Vibha/Software/SeleniumGrid
Use the below command to run selenium-server standalone jar files.
It’s optional to mention the port number at the end of the command. By default, it will use port 4444. It is good practice to mention the port at the end to avoid any conflict between the ports.
4. Open Selenium Console
The server is listening on http://172.30.96.1:4445/ui/index.html which is the same as shown in the above image. This is a dynamic address, so make sure to get the address from the logs when Selenium Grid is started.
The Grid automatically identifies that the WebDrivers for Chrome and Firefox are present on the system.
5. Add dependencies to the project
In this example, we are using a Maven project, so are adding the dependencies to the POM.xml.
Creating an instance of the Remote WebDriver and passing the selenium endpoint and chrome options defined in it.
To run a Remote WebDriver client, we first need to connect to the RemoteWebDriver. We do this by pointing the URL to the address of the server running our tests. In order to customize our configuration, we set desired capabilities.
Below is an example of instantiating a remote WebDriver object pointing to our remote web server running our tests on Chrome, Firefox, and Edge. We have used @Parameters Annotation to pass the browser names to the tests.
import org.openqa.selenium.By;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Parameters;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
public class BaseTest {
protected static ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<RemoteWebDriver>();
public static String remote_url = "http://localhost:4445/";
public Capabilities capabilities;
@Parameters({"browser"})
@BeforeMethod
public void setDriver(String browser) throws MalformedURLException {
System.out.println("Test is running on "+browser);
if(browser.equals("firefox")) {
capabilities = new FirefoxOptions();
} else if (browser.equals("chrome")) {
capabilities = new ChromeOptions();
} else if (browser.equals("edge")) {
capabilities = new EdgeOptions();
}
driver.set(new RemoteWebDriver(new URL(remote_url), capabilities));
driver.get().get("https://opensource-demo.orangehrmlive.com/");
driver.get().manage().window().maximize();
driver.get().manage().timeouts().pageLoadTimeout(Duration.ofSeconds(10));
WebDriverWait wait = new WebDriverWait(driver.get(), Duration.ofSeconds(10));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='divUsername']/span")));
}
public WebDriver getDriver() {
return driver.get();
}
@AfterMethod
public void closeBrowser() {
driver.get().quit();
driver.remove();
}
}
Selenium4ParallelDemo
This class contains the various tests that need to be executed.
InBaseTest class, I created ThreadLocal <>() webdriver (ThreadLocalMap) for thread-safe test execution
I got the TestNG parameter (browser)with @Parameterannotation.
BaseTest returns browser Capabilities based on browser name.
In BaseTest class, thegetDriver()method returns the created driver.
Selenium4ParallelDemoclass extends TestBase class and comprises their test code.
It is very easy to create testng.xml in the case of Eclipse. Right-click on the project, and select TestNG -> Convert to TestNG. It will create a basic testng.xml structure. We need to add the parameter name and value to it.
We are planning to run the test on 3 different browsers, so we are passing the name of the browser from this testng.xml using the parameter. This testng.xml will run the test sequentially. There is a minor change needs to be done in testng.xml that will run the tests parallelly.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test thread-count="5" name="Chrome Test">
<parameter name ="browser" value="chrome"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
<test thread-count="5" name="Firefox Test">
<parameter name ="browser" value="firefox"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
<test thread-count="5" name="Edge Test">
<parameter name ="browser" value="edge"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
8. Run the tests from testng.xml
Right-Click on the testng.xml and selectRun As ->TestNG Suite.
9. Navigate to the sessions tab on the Selenium Grid UI upon running the command
It would reflect an active session.
10. Review the test execution result
There are 2 ways to see if the tests are getting executed or not. First, we can check in the command line. We can see the logs there as shown below.
The complete test execution result can be found in the console too.
The tests are TestNG ones, so we can also check the “Result of Running Suite“ tab also.
11. TestNG Report Generation
TestNG generates the test reports in the test-output folder.
We are interested in 2 reports – index.html and emailable-report.html.
Index.html
The below image shows that the tests are run sequentially.
Emailable-Report.html
This report is the summary report. It contains the summary of all the tests executed like, the number of tests passed, skipped, retried, and failed, and the execution time taken by each test.
Parallel Testing
Update the testng.xml for parallel testing. Add parallel=”tests” in the XML.
Updated testng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="tests">
<test thread-count="5" name="Chrome Test">
<parameter name ="browser" value="chrome"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
<test thread-count="5" name="Firefox Test">
<parameter name ="browser" value="firefox"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
<test thread-count="5" name="Edge Test">
<parameter name ="browser" value="edge"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
The below image shows that the tests were running on multiple browsers at the same time.
The console also shows that tests were started on all three browsers at the same time.
Index.html shows that methods run in chronological order. It can be seen that the setDriver() method for all 3 browsers was the first one to be executed.
Best Practices:
It is advisable to use nodes other than 4444 to run the tests. By using the different port numbers, we prevent the risk that the port is already in use on your system.
It is suggested to use RemoteWebDriver objects, as it is used to set which node (or machine) our test will run against.
Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
TestNG is a well thought Test Framework. It provides a lot of different features which makes the life of a tester a little easy. It happens sometimes that a test execution fails, but the failure is not a product bug, but there can be different reasons for the failure such as the environment is down, third party web service is down, or the browser becomes unresponsive. Imagine a scenario where we need to run a test suite consisting of 100 tests and a few tests failed as a result of a known intermittent environment issue. We know that these tests can pass if rerun a couple of times. So, in this case, the retry functionality of TestNG comes to the rescue. This is one of the best and most frequently used functionality.
In this tutorial let us study how we can implement retry on failed tests in TestNG. In order to achieve this, we have to first understand the org.testng.IRetryAnalyzer interface.
To start with, please add the below dependencies to the Maven Project.
IRetryAnalyzer – It is an interface to implement to be able to have a chance to retry a failed test. The definition of this interface is
public interface IRetryAnalyzer {
/**
* Returns true if the test method has to be retried, false otherwise.
*
* @param result The result of the test method that just ran.
* @return true if the test method has to be retried, false otherwise.
*/
boolean retry(ITestResult result);
}
This method implementation returns true if you want to re-execute your failed test and false if you don’t want to re-execute your test.
When you bind a retry analyzer to a test, TestNG automatically invokes the retry analyzer to determine if TestNG can retry a test case again in an attempt to see if the test that just fails now passes. Here is how you use a retry analyzer:
Bind this implementation to the @Test annotation for e.g., @Test(retryAnalyzer = Retry.class)
Build an implementation of the interface org.testng.IRetryAnalyzer
1. Add IRetryAnalyzer to the @TestAnnotation
First of all, you need to create a class that implements the IRetryAnalyzer like the below example:
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
public class Retry implements IRetryAnalyzer {
int retryCount = 0;
int maxRetryCount = 2;
public boolean retry(ITestResult result) {
if(!result.isSuccess()) { //Check if test is failed
if(retryCount<maxRetryCount) { //Check if the maximum number of test execution is reached
System.out.println("Retrying Test : Re-running " + result.getName() +
" for " + (retryCount+1) + " time(s)."); //Print the number of Retry attempts
retryCount++; //Increase the maxRetryCount by 1
result.setStatus(ITestResult.FAILURE); //Mark test as failed
return true; //Rerun the failed test
} else {
result.setStatus(ITestResult.FAILURE); //TestNG marks last run as failed, if last run is max retry
}
}else {
result.setStatus(ITestResult.SUCCESS); //TestNG parks test as passed when the test test passes
}
return false;
}
}
This example shows that failed test case will run 3 times till it passes. In case it fails the third time, test execution will stop and TestNG will mark this case as failed. We can change the number of tries by changing the value of maxRetryCount.
Using retryAnalyzer attribute in the @Test annotation
The next step is to associate your test cases with IRetryAnalyzer. In order to do this, you need to use the method below.
@Test(retryAnalyzer = Retry.class)
public void verifyLoginPage() {
}
Let us see the complete implementation with the help of the below example.
In the above example, test – verifyLoginPage() will be retried a maximum of 3 times, if the test fails. To run the tests, Right-click on the class and select Run As ->TestNG Suite.
The output of the above program is
2. Implement Interface ITestAnnotationTransformer to retry failed tests
In this case, you would need to implement ITestAnnotationTransformer interface. The implementation of this interface is
public interface IAnnotationTransformer extends ITestNGListener {
/**
* This method will be invoked by TestNG to give you a chance to modify a TestNG annotation read
* from your test classes. You can change the values you need by calling any of the setters on the
* ITest interface.
*
* <p>Note that only one of the three parameters testClass, testConstructor and testMethod will be
* non-null.
*
* @param annotation The annotation that was read from your test class.
* @param testClass If the annotation was found on a class, this parameter represents this class
* (null otherwise).
* @param testConstructor If the annotation was found on a constructor, this parameter represents
* this constructor (null otherwise).
* @param testMethod If the annotation was found on a method, this parameter represents this
* method (null otherwise).
*/
default void transform(
ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
// not implemented
}
The transform method is called for every test during the test run. We can use this listener for our retry analyzer as shown below:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.IAnnotationTransformer;
import org.testng.annotations.ITestAnnotation;
public class RetryListener implements IAnnotationTransformer{
public void transform(ITestAnnotation arg0, Class arg1, Constructor arg2,Method arg3) {
arg0.setRetryAnalyzer(Retry.class);
}
}
Now let us create a class that contains all the tests.
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import io.github.bonigarcia.wdm.WebDriverManager;
public class RetryTests {
WebDriver driver;
@BeforeTest
public void setUp() {
WebDriverManager.chromedriver().setup();
ChromeOptions chromeOptions = new ChromeOptions();
driver = new ChromeDriver(chromeOptions);
driver.get("https://opensource-demo.orangehrmlive.com/");
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
@Test(description = "This test validates title of login functionality")
public void verifyLoginPage() {
String expectedTitle = driver.findElement(By.xpath("//*[@id='logInPanelHeading']")).getText();
System.out.println("Title :" + expectedTitle);
Assert.assertTrue(expectedTitle.equalsIgnoreCase("LOGIN Panel !!"));
}
@Test(description = "This test validates successful login to Home page")
public void verifyHomePage() {
System.out.println("Username Entered");
driver.findElement(By.name("txtUsername")).sendKeys("Admin");
System.out.println("Password Entered");
driver.findElement(By.name("txtPassword")).sendKeys("admin123");
driver.findElement(By.id("btnLogin")).submit();
String newPageText = driver.findElement(By.id("welcome")).getText();
System.out.println("newPageText :" + newPageText);
Assert.assertTrue(newPageText.contains("Welcome"));
}
@AfterTest
public void teardown() {
driver.quit();
}
}
Once we have the implementation of IAnnotationTransformer, we just need to add it as a listener in the testng.xml. Like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<listeners>
<listener class-name="com.example.retrydemo.RetryListener"></listener>
</listeners>
<test name="Test">
<classes>
<class name="com.example.retrydemo.RetryTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Now let us run the tests. Right-click on testng.xml and select Run As -> TestNG Suite.
The output of the above program is
This is pretty much it on this topic. Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
In this tutorial, I am going to build an automation framework to test theSpringboot application with Cucumber, Rest Assured, and TestNG.
What is Springboot?
Spring Boot is an open-source micro-framework maintained by a company called Pivotal. It provides Java developers with a platform to get started with an auto-configurable production-grade Spring application. With it, developers can get started quickly without losing time on preparing and configuring their Spring application.
What is Cucumber?
Cucumber is a software tool that supports behavior-driven development (BDD). Cucumber can be defined as a testing framework, driven by plain English. It serves as documentation, automated tests, and development aid – all in one.
This framework consists of:
Springboot – 2.5.2
Cucumber – 7.3.4
Java 11
TestNG – 7.3.4
Maven – 3.8.1
RestAssured – 5.1.1
Steps to setup Cucumber Test Automation Framework for API Testing using Rest-Assured
Add SpringbootTest, Rest-Assured, JUnit, and Cucumber dependencies to the project
Create a source folder src/test/resources and create a feature file under src/test/resources
Create the Step Definition class or Glue Code for the Test Scenario under the src/test/java directory
Create a Cucumber Runner class under the src/test/java directory
Run the tests from Cucumber Test Runner
Run the tests from Command Line
Run the tests from TestNG
Generation of TestNG Reports
Cucumber Report Generation
Below is the structure of a SpringBoot application project
We need the below files to create a SpringBoot Application.
SpringBootRestServiceApplication.java
The Spring Boot Application class is generated with Spring Initializer. This class acts as the launching point for the application.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootRestServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootRestServiceApplication.class, args);
}
}
Student.java
This is JPA Entity for Student class
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Entity
public class Student {
@Id
@GeneratedValue
private Long id;
@NotNull
@Size(min = 4, message = "Name should have atleast 4 characters")
private String name;
@NotBlank(message = "passportNumber is mandatory")
private String passportNumber;
public Student() {
super();
}
public Student(Long id, String name, String passportNumber) {
super();
this.id = id;
this.name = name;
this.passportNumber = passportNumber;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassportNumber() {
return passportNumber;
}
public void setPassportNumber(String passportNumber) {
this.passportNumber = passportNumber;
}
}
StudentRepository.java
This is JPA Repository for Student. This is created using Spring Data JpaRepository.
Spring Boot automatically loads the application.properties whenever it starts up. You can de-reference values from the property file in the java code through the environment.
spring.jpa.defer-datasource-initialization=true
data.sql
Data is loaded from data.sql into the Student table. Spring Boot would execute this script after the tables are created from the entities.
insert into student values(10001,'Annie', 'E1234567');
insert into student values(20001,'John', 'A1234568');
insert into student values(30001,'David','C1232268');
insert into student values(40001,'Amy','D213458');
Test Automation Framework Implementation
Step 1 – Add SpringbootTest, Cucumber, Rest-Assured, and TestNG dependencies to the project (Maven project)
Step 2 – Create a source folder src/test/resources and create a feature file under src/test/resources
By default, the Maven project has an src/test/java directory only. Create a new Source Folder under src/test with the name of resources. Create a folder name as Features within the src/test/resources directory.
Create a feature file to test the Springboot application. Below is a sample feature file.
Feature: Verify springboot application using Cucumber and TestNG
@ReceiveUserDetails
Scenario Outline: Send a valid Request to get user details
Given I send a request to the URL "/students" to get user details
Then The response will return status 200
And The response contains id <studentID> and names "<studentNames>" and passport_no "<studentPassportNo>"
Examples:
|studentID |studentNames |studentPassportNo|
|10001 |Annie |E1234567 |
|20001 |John |A1234568 |
|30001 |David |C1232268 |
|40001 |Amy |D213458 |
@CreateUser
Scenario: Send a valid Request to create a user
Given I send a request to the URL "/students" to create a user with name "Annie" and passportNo "E1234567"
Then The response will return status 201
And Resend the request to the URL "/students" and the response returned contains name "Annie" and passport_no "E1234567"
Step 3 – Create the Step Definition class or Glue Code for the Test Scenario under src/test/java
The corresponding step definition file of the above feature file is shown below.
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import org.json.JSONObject;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.spring.CucumberContextConfiguration;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse;
import io.restassured.specification.RequestSpecification;
@CucumberContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringbootDefinitions {
private final static String BASE_URI = "http://localhost";
@LocalServerPort
private int port;
private ValidatableResponse validatableResponse, validatableResponse1;
private void configureRestAssured() {
RestAssured.baseURI = BASE_URI;
RestAssured.port = port;
}
protected RequestSpecification requestSpecification() {
configureRestAssured();
return given();
}
@Given("I send a request to the URL {string} to get user details")
public void getStudentDetails(String endpoint) throws Throwable {
validatableResponse = requestSpecification().contentType(ContentType.JSON).when().get(endpoint).then();
System.out.println("RESPONSE :" + validatableResponse.extract().asString());
}
@Given("I send a request to the URL {string} to create a user with name {string} and passportNo {string}")
public void createStudent(String endpoint, String studentName, String studentPassportNumber) throws Throwable {
JSONObject student = new JSONObject();
student.put("name", studentName);
student.put("passportNumber", studentPassportNumber);
validatableResponse = requestSpecification().contentType(ContentType.JSON).body(student.toString()).when()
.post(endpoint).then();
System.out.println("RESPONSE :" + validatableResponse.extract().asString());
}
@Then("The response will return status {int}")
public void verifyStatusCodeResponse(int status) {
validatableResponse.assertThat().statusCode(equalTo(status));
}
@Then("The response contains id {int} and names {string} and passport_no {string}")
public void verifyResponse(int id, String studentName, String passportNo) {
validatableResponse.assertThat().body("id", hasItem(id)).body(containsString(studentName))
.body(containsString(passportNo));
}
@Then("Resend the request to the URL {string} and the response returned contains name {string} and passport_no {string}")
public void verifyNewStudent(String endpoint, String studentName, String passportNo) {
validatableResponse1 = requestSpecification().contentType(ContentType.JSON).when().get(endpoint).then();
System.out.println("RESPONSE :" + validatableResponse1.extract().asString());
validatableResponse1.assertThat().body(containsString(studentName)).body(containsString(passportNo));
}
}
To make Cucumber aware of your test configuration you can annotate a configuration class on your glue path with @CucumberContextConfiguration and with one of the following annotations: @ContextConfiguration, @ContextHierarchy, or @BootstrapWith.It is imported from:
By default, @SpringBootTest does not start the webEnvironment to refine further how your tests run. It has several options: MOCK(default), RANDOM_PORT, DEFINED_PORT, NONE.
RANDOM_PORT loads a WebServerApplicationContext and provides a real web environment. The embedded server is started and listens on a random port. LocalServerPort is imported from the package:
Step 4 – Create a Cucumber TestNG Runner class under src/test/java
A runner will help us to run the feature file and acts as an interlink between the feature file and StepDefinition Class. The TestRunner should be created within the directory src/test/java.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(features = {"src/test/resources/Features"}, glue = {"com.example.demo.definitions"})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
The @CucumberOptions annotation is responsible for pointing to the right feature package, configuring the plugin for a better reporting of tests in the console output, and specifying the package where extra glue classes may be found. We use it to load configuration and classes that are shared between tests.
Step 5 – Run the tests from Cucumber Test Runner
You can execute the test script by right-clicking onTestRunner class -> Run As TestNG in Eclipse.
In case you are using IntelliJ, select “Run CucumberRunnerTests“.
SpringBootTest creates an application context containing all the objects we need for the Integration Testing It, starts the embedded server, creates a web environment, and then enables methods to do Integration testing.
Step 6 – Run the tests from Command Line
Use the below command to run the tests through the command line.
mvn clean test
Step 7 – Run the tests from TestNG
Create a testng.xml in the project as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name = "Suite1">
<test name = "SpringBoot Cucumber TestNG Demo">
<classes>
<class name = "com.example.demo.runner.CucumberRunnerTests"/>
</classes>
</test>
</suite>
Step 8 – Generation of TestNG Reports
TestNG generates various types of reports underthe test-output folder like emailable-report.html, index.html, testng-results.xml.
We are interested in the “emailable-report.html” report. Open “emailable-report.html”, as this is an HTML report, and open it with the browser. The below image shows emailable-report.html.
TestNG also produce “index.html” report, and it resides under test-output folder. The below image shows index.html report.
Step 9 – Cucumber Report Generation
Add cucumber.properties under src/test/resources and add the below instruction in the file.
cucumber.publish.enabled=true
The link to the Cucumber Report is present in the execution status.
Below is the image of the Cucumber Report generated using the Cucumber Service.
Complete Source Code: Refer to GitHub for the source code.
Congratulations!! We are able to build a test framework to test the SpringBoot application using Cucumber, Rest Assured, and TestNG.
A batch file (.bat) is used in DOS and Windows, which is an unformatted text file that consists of a series of commands to be executed by the command line interpreter.
Prerequisite:
Selenium
TestNG
Maven
Java 11
Maven Complier Plugin
Maven Surefire Plugin
Notepad
Let us first create some tests in a class.
import static org.testng.Assert.assertTrue;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class TestNGRunFromCommandLine {
WebDriver driver;
@BeforeTest
public void setUp() {
System.setProperty("webdriver.gecko.driver",
"C:\\Users\\Vibha\\Software\\geckodriver\\geckodriver.exe");
driver = new FirefoxDriver();
driver.get("https://opensource-demo.orangehrmlive.com/");
driver.manage().window().maximize();
}
@Test(description = "This test validates title of login functionality", priority = 0)
public void verifyLoginPage() {
String expectedTitle = driver.findElement(By.xpath("//*[@id='logInPanelHeading']")).getText();
System.out.println("Title :" + expectedTitle);
assertTrue(expectedTitle.equalsIgnoreCase("LOGIN Panel12"));
}
@Test(description = "This test validates successful login to Home page", priority = 1)
public void verifyHomePage() throws InterruptedException {
System.out.println("Username Entered");
driver.findElement(By.name("txtUsername")).sendKeys("Admin");
System.out.println("Password Entered");
driver.findElement(By.name("txtPassword")).sendKeys("admin123");
driver.findElement(By.id("btnLogin")).submit();
Thread.sleep(2000);
String newPageText = driver.findElement(By.xpath("//*[@id='content']/div/div[1]/h1")).getText();
System.out.println("newPageText :" + newPageText);
assertThat(newPageText, containsString("Dashboard"));
}
@AfterTest
public void teardown() {
driver.quit();
}
}
The below is the testng.xml file which has a class that we have created above and we will be invoking this xml file using batch file (.bat).
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Test">
<classes>
<class name="TestNG_Demo.TestNGRunFromCommandLine"/>
</classes>
</test>
</suite>
How to create a batch file?
Step 1: Open notepad Step 2: Paste the below lines of code – You may need to add your project location. In the example, project location is set as C:\Users\Vibha\Projects\Vibha_Personal\ParallelTestsTestNG Step 3: Save the file as ‘TestNGProject.bat’ in location that you want to save.
cd C:\Users\Vibha\Projects\Vibha_Personal\ParallelTestsTestNG
mvn compile test
Now, to run the tests, double-click on the TestNGProject.bat file and all the commands mentioned in the file will be executed one by one.
As we know, TestNG generates a lot of Reports automatically. We are going to look into 2 reports – emailable-report.html and index.html. The reports are generated under surefire-reports folder within the target directory.
Emailable-Report.html
Index.html
Hope this article helps you to invoke your tests using .bat file.