Gradle Project with Cucumber, Selenium and JUnit4

HOME

The previous tutorial explained the Integration of Cucumber with Selenium and JUnit4 in a Maven Project. This tutorial explains the test automation framework based on Gradle, Cucumber, Selenium, and JUnit4.

Pre Requisite:

  1. Java 8 or above installed
  2. Eclipse or IntelliJ IDE installed
  3. Gradle Installed
  4. 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 JUnit4. This framework consists of:-

  1. Cucumber Java- 7.6.0
  2. Cucumber JUnit– 7.6.0
  3. Java 11
  4. JUnit4 – 4.13.2
  5. Gradle – 7.5.1
  6. Selenium – 4.3.0

Project Structure

Steps to set up Cucumber Test Automation Framework with Selenium and TestNG

  1. Download and Install Java on the system
  2. Download and setup Eclipse IDE on the system
  3. Install and setup Gradle
  4. Install Cucumber Eclipse Plugin (For Eclipse IDE)
  5. Create a new Gradle Project
  6. Add SeleniumJUnit4, and Cucumber dependencies to the build.gradle
  7. Create a feature file under src/test/resources
  8. Create the classes for locators, actions, and utilities in src/main/java
  9. Create the Step Definition class or Glue Code in src/test/java
  10. Create a Hook class to contain the initialization and closing of the browser in src/test/java
  11. Create a JUnit4 Cucumber Runner class in src/test/java
  12. Run the tests from Command Line
  13. 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.

If you want to create the Gradle project from Eclipse IDE, click here to know How to create a Gradle Java project. Below is the structure of the Gradle project.

Step 6 – Add Selenium, JUnit4, and Cucumber dependencies to the build.gradle

Add below mentioned Selenium, JUnit4, and Cucumber dependencies to the project.

/*
 * 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'
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

java {
    sourceCompatibility = 11
    targetCompatibility = 11
}

dependencies {

    testImplementation 'io.cucumber:cucumber-java:7.6.0'
    testImplementation 'io.cucumber:cucumber-junit:7.6.0'  
    
    // Use JUnit test framework.
    testImplementation 'junit:junit:4.13.2'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:30.1.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'
}

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'
            ]
        }

    }
}

I have added WebDriverManager dependency to the POM.xml to download the driver binaries automatically. To know more about this, please refer to this tutorial – How to manage driver executables using WebDriverManager.

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.

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               |
  | $$$$$$     | %%%%%     | Invalid credentials               |
  
   @MissingUsername @FailedTest
   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 for empty username as "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) {
 
        // Fill user name
    	loginPageLocators.userName.sendKeys(strUserName);
 
        // Fill password
    	loginPageLocators.password.sendKeys(strPassword);
 
        // Click Login button
    	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.junit.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) {
 
        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(expectedErrorMessage,objLogin.getErrorMessage());
    }
     
    @Then("User should be able to see error message for empty username as {string}")
    public void verifyErrorMessageForEmptyUsername(String expectedErrorMessage) {
    	 
        Assert.assertEquals(expectedErrorMessage,objLogin.getMissingUsernameText());
 
    }   
}

Step 10 – Create a Hook class to contain the initialization and closing of the browser in src/test/java

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 JUnit 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 org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)
@CucumberOptions(tags = "", features = {"src/test/resources/features/LoginPage.feature"}, glue = {"com.example.definitions"})
   
public class CucumberRunnerTests {
   
}

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!!

Gradle – Extent Report Version 5 for Cucumber, Selenium, and TestNG

HOME

The previous tutorial explained the generation of Extent Reports Version 5 for Cucumber 7 and TestNG in a Maven project. In this tutorial, I will explain the steps to create an Extent Report Version 5 for Cucumber, Selenium, and TestNG in a Gradle project.

Prerequisite

  1. Java 8 or above installed
  2. Eclipse or IntelliJ IDE installed
  3. Gradle Installed
  4. 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.

  1. Cucumber Java- 7.6.0
  2. Cucumber JUnit– 7.6.0
  3. Java 11
  4. TestNG – 7.6.0
  5. Gradle – 7.5.1
  6. Selenium – 4.3.0
  7. ExtentReport – 5.0.9
  8. GrassHopper Cucumber Adapter – 1.7.0

Implementation Steps

There is a tutorial that explains the Integration of Cucumber, Selenium, and TestNG in a Gradle project. Please refer to this tutorial – Gradle Project with Cucumber, Selenium and TestNG.

Step 1 – Add Extent Report dependency to the build.gradle

To create an Extent Report, we need to add the below-mentioned dependency in the build.gradle

implementation 'tech.grasshopper:extentreports-cucumber7-adapter:1.7.0'
implementation 'com.aventstack:extentreports:5.0.9'  

Step 2 – Add ExtentCucumberAdapter plugin to task cucumber

task cucumber() {
    dependsOn assemble, compileTestJava
    doLast {
        javaexec {         
            main = "io.cucumber.core.cli.Main"
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
            args = ['--plugin', 'pretty', 
            '--plugin', 'io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm',
            '--plugin', 'com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:',
            '--glue', 'com.example.definitions', 'src/test/resources']
        }
    }
}

Step 3 – Add Cucumber, Selenium, and TestNG dependencies in build.gradle

dependencies {

    testImplementation 'io.cucumber:cucumber-java:7.6.0'
    testImplementation 'io.cucumber:cucumber-junit:7.6.0'
    
     //TestNG  
     testImplementation 'org.testng:testng:7.6.0'
    
    //ExtentReport    
    implementation 'tech.grasshopper:extentreports-cucumber7-adapter:1.7.0'
    implementation 'com.aventstack:extentreports:5.0.9' 

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:30.1.1-jre'
    implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
    implementation 'io.github.bonigarcia:webdrivermanager:5.3.0'
}

The complete build.gradle is shown below:

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
}

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'
      
    //ExtentReport    
     implementation 'tech.grasshopper:extentreports-cucumber7-adapter:1.7.0'
     implementation 'com.aventstack:extentreports:5.0.9'  
      
     //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 {         
            main = "io.cucumber.core.cli.Main"
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
            args = ['--plugin', 'pretty', 
            '--plugin', 'io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm',
            '--plugin', 'com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:',
            '--glue', 'com.example.definitions', 'src/test/resources']
        }
    }
}

Step 4 – Create Locator and Action classes and Step Definition corresponding to the feature file

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 5 – Create extent.properties file in the resources folder and paste the below code

#Extent Report
extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html
 
#PDF Report
extent.reporter.pdf.start=true
extent.reporter.pdf.out=PdfReport/ExtentPdf.pdf
 
#HTML Report
extent.reporter.html.start=true
extent.reporter.html.out=HtmlReport/ExtentHtml.html
 
#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
 
#Screenshot
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
 
#Base64
extent.reporter.spark.base64imagesrc=true
 
#System Info
systeminfo.os=windows
systeminfo.version=10

Step 6 – Execute the Tests

Go to the app project and run the tests, using the below command

gradle cucumber

The output of the above program is

Step 7 – View the ExtentReports

Refresh the project and will see a new folder – SparkReport_ which further contains 4 folders -Html Report, Pdf Report, Reports, and Screenshots.

The Extent Report will be present in the Report’s folder with the name Spark.html. PDF Report is present in the Pdf Report folder and HTML Report is present in the HTML report folder. We can see that the Screenshot’s folder is empty because we have used the base64imagesrc feature, which resulted in no physical screenshots. The screenshots are embedded in the reports.

Right-click and open the ExtentHtml.html report with the 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.

ExtentHtml.html

The failed test has a screenshot embedded in it. Double-click on mase64image, and it will open the screenshot in full screen.

Screenshot of failed Test Case

PDF Report

To know more about PDF Report generation, please refer to this tutorial – PDF ExtentReport for Cucumber and TestNG.

Spark Report

Right-click and open the Spark.html report with the Web Browser.

Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!

Gradle Project with Cucumber, Selenium and TestNG

HOME

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:

  1. Java 8 or above installed
  2. Eclipse or IntelliJ IDE installed
  3. Gradle Installed
  4. 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:-

  1. Cucumber Java- 7.6.0
  2. Cucumber TestNG – 7.6.0
  3. Java 11
  4. TestNG – 7.6.0
  5. Gradle – 7.5.1
  6. Selenium – 4.3.0

Project Structure

Steps to set up Cucumber Test Automation Framework with Selenium and TestNG

  1. Download and Install Java on the system
  2. Download and setup Eclipse IDE on the system
  3. Install and setup Gradle
  4. Install Cucumber Eclipse Plugin (For Eclipse IDE)
  5. Create a new Gradle Project
  6. Add SeleniumTestNG, and Cucumber dependencies to the build.gradle
  7. Create a feature file under src/test/resources
  8. Create the classes for locators, actions and utilities in src/main/java
  9. Create the Step Definition class or Glue Code in src/test/java
  10. Create a Hook class to contain the initialization and closing of browser in src/test/java
  11. Create a TestNG Cucumber Runner class in src/test/java
  12. Run the tests from Command Line
  13. 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.

If you want to create the Gradle project from Eclipse IDE, click here to know How to create a Gradle Java project. Below is the structure of the Gradle project.

Step 6 – Add Selenium, TestNG, and Cucumber dependencies to the build.gradle

Add below mentioned Selenium, TestNG, and Cucumber dependencies to the project.

I have added WebDriverManager dependency to the POM.xml to download the driver binaries automatically. To know more about this, please refer to this tutorial – How to manage driver executables using WebDriverManager.

/*
 * 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!!

Gradle – Allure Report for Selenium and JUnit4

HOME

The previous tutorial explained the generation of Allure Report for Selenium and JUnit4 in a Maven Project. This tutorial explains the generation of Allure Report for Selenium and JUnit4 in a Gradle project.

Pre-Requisite:

  1. Java 11 installed
  2. Gradle installed
  3. Eclipse or IntelliJ installed
  4. Allure installed
  5. Environment variables JAVA_HOME , GRADLE_HOME and ALLURE_HOME correctly configured

This framework consists of:

  1. Selenium – 4.4.0
  2. Java 11
  3. JUnit – 4.13.2
  4. Gradle – 7.5.1
  5. Allure Report – 2.11.0
  6. Allure JUnit4 – 2.19.0

Project Structure

Implementation Steps

  1. Add Selenium, JUnit4 and Allure-JUnit4 dependencies in build.gradle
  2. Create Pages and Test Code for the pages
  3. Execute the Tests through Command Line
  4. Generate Allure Report

There is another tutorial that explain the steps to create a Gradle Project with JUnit4 – please refer to this tutorial – How to create Gradle project with Selenium and JUnit4

Step 1 – Add Selenium, JUnit4, and Allure-JUnit4 dependencies in 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 JUnit test framework.
    testImplementation 'junit:junit:4.13.2'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:30.1.1-jre'
    implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
    implementation 'io.github.bonigarcia:webdrivermanager:5.3.0'
    implementation 'io.qameta.allure:allure-junit4:2.19.0'
}

application {
    // Define the main class for the application.
    mainClass = 'com.example.App'
}

test {
    useJUnit {   
    }

    testLogging {
        events "passed", "skipped", "failed"
        showStandardStreams = true
    }

    systemProperties System.properties
}

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 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 when username is blank
   public String getMissingUsernameText() {
        return missingUsernameErrorMessage.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.junit.After;
import org.junit.Before;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import io.github.bonigarcia.wdm.WebDriverManager;
import io.qameta.allure.Step;

public class BaseTests {
	
	public WebDriver driver;
	public final static int TIMEOUT = 10;
    
 
	@Before
    @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")
    @After
    public void tearDown() {
        driver.quit();
    }   
}

LoginTests

import org.junit.Assert;
import org.junit.Ignore;
import org.junit.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("Test Description : Login Test with invalid credentials")
    public void invalidCredentials() {
   
	    LoginPage objLoginPage = new LoginPage(driver);
    	objLoginPage.login("Admin", "admin123$$");
    	 
    	// Verify Error Message
    	 Assert.assertEquals("Invalid credentials",objLoginPage.getErrorMessage());
    
    }
    
	@Severity(SeverityLevel.BLOCKER)
    @Test
	@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("Employee Information",objHomePage.getHomePageText());
    
    }
    
	@Severity(SeverityLevel.NORMAL)
    @Test
	@Description("Test Description : Login Test with missing username")
    public void missingUsername() {
   
	    LoginPage objLoginPage = new LoginPage(driver);
    	objLoginPage.login("", "admin123");
    	     	
    	// Verify Error Message
   	     Assert.assertEquals("Invalid credentials",objLoginPage.getMissingUsernameText());
   	     
    
    }
	
	@Severity(SeverityLevel.NORMAL)
    @Test @Ignore
	@Description("Test Description : Login Test with missing password")
    public void missingPassword() {
   
	    LoginPage objLoginPage = new LoginPage(driver);
    	objLoginPage.login("admin", "");
    	    	
    	// Verify Error Message
   	     Assert.assertEquals("Invalid credentials",objLoginPage.getMissingPasswordText());
    
    }    
   
}

Step 3 – Execute the Tests through Command Line

Note:- As you can see my project has two parts – app and GradleSeleniumJUnit4Demo.

Go to the app project and run the tests, using the below command

gradle clean test

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 the build 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.

  1. Statistics – overall report statistics.
  2. Launches – if this report represents several test launches, statistics per launch will be shown here.
  3. Behaviors – information on results aggregated according to stories and features.
  4. Executors – information on test executors that were used to run the tests.
  5. History Trend – if tests accumulated some historical data, its trend will be calculated and shown on the graph.
  6. Environment – information on the test environment.

Categories in Allure Report

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: statuses breakdown or severity and duration diagrams.

Timeline in Allure Report

Timeline tab visualizes retrospective of tests 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 and Story tags.

Packages in Allure Report

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!!

How to create Gradle project with Selenium and JUnit4

HOME

The previous tutorial explained How to create Java Gradle project in Eclipse. In this tutorial, I will explain how we can set up a Gradle project with Selenium and JUnit4.

This framework consists of:

  1. Java 8 or above
  2. JUnit– 4.13.2
  3. Gradle – 7.5.1 (Build Tool)
  4. Selenium – 4.3.0

Steps to set up Gradle Java Project for Selenium and TestNG

  1. Download and Install Java on the system
  2. Download and setup Eclipse IDE on the system
  3. Setup Gradle on System
  4. Create a new Gradle Project
  5. Add Selenium and JUnit4 dependencies to the Gradle project
  6. Create Pages and Test Code for the pages
  7. Run the tests from JUnit
  8. Run the tests from Command Line
  9. Gradle Report generation

Project Structure

Implementation Steps

Step 1- Download and Install Java

Selenium needs 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 Gradle

To build a test framework, we need to add several dependencies to the project. This can be achieved by any build tool. I have used Gradle Build Tool. Click here to know How to install Gradle.

Step 4 – Create a new Gradle Project

If you want to create the Gradle project from Eclipse IDE, click here to know How to create a Gradle Java project.

Step 5 – Add Selenium and JUnit4 dependencies to the Gradle project

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

java {
    sourceCompatibility = 11
    targetCompatibility = 11
}

dependencies {
    // Use JUnit test framework.
    testImplementation 'junit:junit:4.13.2'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:30.1.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'
}

test {
    useJUnit {
    
    }

    testLogging {
        events "passed", "skipped", "failed"
        showStandardStreams = true
    }

    systemProperties System.properties
    reports.html.setDestination(file("$projectDir/GradleReports"))
}

Step 6 – Create Pages and Test Code for the pages

We have used 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.

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 when username is blank
   public String getMissingUsernameText() {
        return missingUsernameErrorMessage.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.junit.After;
import org.junit.Before;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import io.github.bonigarcia.wdm.WebDriverManager;

public class BaseTests {
	
	public WebDriver driver;
	public final static int TIMEOUT = 10;
    
 
	@Before
    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));

    }
 
    @After
    public void tearDown() {
        driver.quit();
    }
    
}

LoginTests

import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;

public class LoginTests extends BaseTests{
	 
    @Test
    public void invalidCredentials() {
   
	    LoginPage objLoginPage = new LoginPage(driver);
    	objLoginPage.login("Admin", "admin123$$");
    	 
    	// Verify Error Message
    	 Assert.assertEquals("Invalid credentials",objLoginPage.getErrorMessage());
    
    }
    
    @Test
    public void gotoHomePage() {
   
	    LoginPage objLoginPage = new LoginPage(driver);
    	objLoginPage.login("Admin", "admin123");
    	 
    	HomePage objHomePage = new HomePage(driver);
    	
    	// Verify Home Page
    	 Assert.assertEquals("Employee Information",objHomePage.getHomePageText());
    
    }
    
    @Test
    public void missingUsername() {
   
	    LoginPage objLoginPage = new LoginPage(driver);
    	objLoginPage.login("", "admin123");
    	     	
    	// Verify Error Message
   	     Assert.assertEquals("Invalid credentials",objLoginPage.getMissingUsernameText());
   	     
    
    }
	
    @Test @Ignore
    public void missingPassword() {
   
	    LoginPage objLoginPage = new LoginPage(driver);
    	objLoginPage.login("admin", "");
    	    	
    	// Verify Error Message
   	     Assert.assertEquals("Invalid credentials",objLoginPage.getMissingPasswordText());
    
    }    
   
}

Step 7 – Run the tests from JUnit

Right-click on the Tests and select Run As -> JUnit Test

The output of the above program is shown below.

Step 8 – Run the tests from Command Line

Note:- As you can see, my project has two parts – GradleSeleniumJUnit4Demo and GradleSeleniumJUnit4Demo-app.

Go to the app project and run the tests, using the below command

gradle clean test

The output of the test execution is

Step 9 – Gradle Report generation

Once the test execution is finished, refresh the project. We will see a folder – GradleReports. This report is generated when the tests are executed through the command line.

This folder contains index.html.

Right-click on index.html and select open with Web Browser. This report shows the summary of all the tests executed. As you can see that Failed tests are selected (highlighted in blue), so the name of the test failed along with the class name is displayed here.

Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!

ExtentReports Version 5 for Cucumber 6 and JUnit4

HOME

The previous tutorial explained the steps to generate ExtentReports for Cucumber with TestNG. We can generate ExtentReports for Cucumber with JUnit4 also. This tutorial explains the steps that need to be followed to generate an ExtentReports Version5.

Pre-Requisite:

  1. Java 8 or higher is needed for ExtentReport5
  2. Maven or Gradle
  3. JAVA IDE (like Eclipse, IntelliJ, or soon)
  4. JUnit4 installed
  5. Cucumber Eclipse plugin (in case using Eclipse)

Project Structure

Step 1 – Add Maven dependencies to the POM

Add ExtentReport dependency

<dependency>
    <groupId>com.aventstack</groupId>
    <artifactId>extentreports</artifactId>
    <version>5.0.9</version>
</dependency>

Add tech grasshopper maven dependency for Cucumber

<dependency>
    <groupId>tech.grasshopper</groupId>
    <artifactId>extentreports-cucumber6-adapter</artifactId>
    <version>2.13.0</version>
</dependency>

The complete POM.xml will look like as shown below with other Selenium and JUnit4 dependencies.

 <properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<selenium.version>4.3.0</selenium.version>
		<cucumber.version>6.11.0</cucumber.version>
		<extentreports.cucumber6.adapter.version>2.13.0</extentreports.cucumber6.adapter.version>
		<extentreports.version>5.0.9</extentreports.version>
		<junit.version>4.13.2</junit.version>
		<apache.common.version>2.4</apache.common.version>
		<webdrivermanager.version>5.2.1</webdrivermanager.version>
		<maven.compiler.plugin.version>3.10.1</maven.compiler.plugin.version>
		<maven.surefire.plugin.version>3.0.0-M7</maven.surefire.plugin.version>
		<maven.compiler.source.version>11</maven.compiler.source.version>
		<maven.compiler.target.version>11</maven.compiler.target.version>
	</properties>

	<dependencies>

		<dependency>
			<groupId>io.cucumber</groupId>
			<artifactId>cucumber-java</artifactId>
			<version>${cucumber.version}</version>
		</dependency>

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

		<!-- https://mvnrepository.com/artifact/tech.grasshopper/extentreports-cucumber6-adapter -->
		<dependency>
			<groupId>tech.grasshopper</groupId>
			<artifactId>extentreports-cucumber6-adapter</artifactId>
			<version>${extentreports.cucumber6.adapter.version}</version>
		</dependency>

		<!-- Extent Report -->
		<dependency>
			<groupId>com.aventstack</groupId>
			<artifactId>extentreports</artifactId>
			<version>${extentreports.version}</version>
		</dependency>

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

		<!-- Apache Common -->
		<dependency>
			<groupId>org.apache.directory.studio</groupId>
			<artifactId>org.apache.commons.io</artifactId>
			<version>${apache.common.version}</version>
		</dependency>
		
		<!-- Selenium -->
		<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-java</artifactId>
			<version>${selenium.version}</version>
		</dependency>

		<!-- Web Driver Manager -->
		<dependency>
			<groupId>io.github.bonigarcia</groupId>
			<artifactId>webdrivermanager</artifactId>
			<version>${webdrivermanager.version}</version>
		</dependency>
	</dependencies>
	
	<build>
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>${maven.compiler.plugin.version}</version>
         <configuration>
             <source>${maven.compiler.source.version}</source> <!--For JAVA 8 use 1.8-->
			 <target>${maven.compiler.target.version}</target> <!--For JAVA 8 use 1.8-->
            <encoding>UTF-8</encoding>          
         </configuration>
       </plugin>                
       </plugins>
   </build>

</project>

Step 2: Create a feature file in src/test/resources

Below is a sample feature file

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 successfully 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      | Admin123  | Invalid credentials               |
  
   
  @ForgetPassword  
   Scenario: Verify Forget Password Functionality
   
    Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
    When User clicks on Forgot your password link
    Then User should be able to navigate to new page of title "Forgot Your Password?"

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:

extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html

#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss

#Screenshot
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/

Step 4: Create a Helper class in src/main/java

We have used Page Object Model with Cucumber and TestNG.

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. 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 3 locator classes – LoginPageLocators, HomePageLocators, and ForgetPasswordPageLocators.

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='forgotPasswordLink']/a")
    public  WebElement forgotPasswordLink;
  
}

HomePageLocators

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;

public class HomePageLocators {

	@FindBy(id = "welcome")
	public  WebElement homePageUserName;
 
}

ForgetPasswordPageLocators

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;

public class ForgetPasswordPageLocators {
	
	@FindBy(xpath = "//*[@id='content']/div[1]/div[2]/h1")
    public WebElement forgotPasswordPageHeading;
	
}

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 3 action classes – LoginPageActions, HomePageActions, and ForgetPasswordPageActions.

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.junit.locators.LoginPageLocators;
import com.example.junit.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();
    }
    
    // Click on forgotYourPassword Link
    public void clickOnForgotPasswordLink() {
    	loginPageLocators.forgotPasswordLink.click();
    }
 
    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.junit.locators.HomePageLocators;
import com.example.junit.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();
    }

}

ForgetPasswordPageActions

import org.openqa.selenium.support.PageFactory;
import com.example.junit.locators.ForgetPasswordPageLocators;
import com.example.junit.utils.HelperClass;

public class ForgetPasswordPageActions {
	
	ForgetPasswordPageLocators forgetPasswordPageLocators = null;
	
	public ForgetPasswordPageActions() {
		this.forgetPasswordPageLocators = new ForgetPasswordPageLocators();
		
		PageFactory.initElements(HelperClass.getDriver(), forgetPasswordPageLocators);
	}

	public String getHeading() {
				return forgetPasswordPageLocators.forgotPasswordPageHeading.getText();
		
	}
}

Step 7: Create Step Definition file in src/test/java

Create the corresponding Step Definition file of the feature file.

LoginPageDefinitions

import org.junit.Assert;
import com.example.junit.actions.ForgetPasswordPageActions;
import com.example.junit.actions.HomePageActions;
import com.example.junit.actions.LoginPageActions;
import com.example.junit.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();
    ForgetPasswordPageActions objForgotPasswordPage = new ForgetPasswordPageActions();
		
    @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
        
    }
    
    @When("User clicks on Forgot your password link")
    public void clickOnForgotPasswordLink() {
    	
    	objLogin.clickOnForgotPasswordLink();
    	
    }
   
    @Then("User should be able to login successfully 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 navigate to new page of title {string}")
    public void verifyForgotPasswordPage(String heading) {
    	
    	Assert.assertEquals(objForgotPasswordPage.getHeading(),heading);
    	
    }
     
}

Step 8: Create Hook class in src/test/java

Create the hook class 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 org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import com.example.junit.utils.HelperClass;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;

public class Hooks {
	
	@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 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 the cucumber. Please add the following text as a plugin to the CucumberOptions as described below.

plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"}

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.junit.Cucumber;
import org.junit.runner.RunWith;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)
@CucumberOptions(tags = "", features = "src/test/resources/features/LoginPage.feature", glue = "com.example.junit.definitions",
plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"})

public class CucumberRunnerTests {

}

Step 10: Execute the code

Right Click on the Runner class and select Run As -> JUnit Test.

Below is the screenshot of the Console. 

Step 11: View 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 on Spark.html and select 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. As we can see that a screenshot is attached to the failed tests here.

Congratulation!! We are able to create an Extent Report for Cucumber and JUnit4. Happy Learning!!!

Selenium 4 Grid : How to use Standalone Grid

HOME

Selenium Grid routes command received by the client to distant browser instances, allowing WebDriver scripts to be run on remote machines (virtual or real). Its goal is to make running tests in parallel on numerous machines as simple as possible.

Selenium Grid allows us to run tests on numerous workstations in parallel and centrally manage diverse browser versions and setups (instead of in each individual test).

Selenium Grid 4 makes use of a variety of new technologies to make scaling up easier while still allowing for local execution.

Selenium Grid 4 is a brand-new implementation that doesn’t use the same codebase as the previous version.

A Selenium Grid is made up of several components. Using a Grid role, you can start each one individually or all of them at once, depending on your needs.

Why do we need Selenium Grid?

Imagine we need to run 1000 tests and it takes 20 hrs to run these tests. With the help pf Selenium Grid, we can have 4 different machines (VMs or separate physical machines) to run those tests. We can roughly reduce the execution time to one-fourth, which means the test execution will be finished in 5 hrs.

Grid is also used to support running tests against multiple runtime environments, specifically, against different browsers at the same time. For example, a ‘grid’ of virtual machines can be set up with each supporting a different browser that the application to be tested must support. So, machine 1 has Google Chrome, machine 2, has Edge, and machine 3 has the latest Firefox. When the test suite is run, Selenium-Grid receives each test-browser combination and assigns each test to run against its required browser. Grid makes cross-browser and parallel testing very easy.

1. Standalone

The term “standalone” refers to the combination of all components that, in the view of the user, operate as if they were one. After launching it in Standalone mode, you’ll have a fully functional Grid of one.

Standalone is also the quickest way to get a Selenium Grid up and running. The server will be listening on http://localhost:4444 by default, and you should use that URL in your RemoteWebDriver tests. The server will look for available drivers in the System PATH and utilize them.

java -jar selenium-server-4.1.2.jar standalone

2. Hub and Nodes

It enables the classic Hub & Node(s) setup. These roles are suitable for small and middle-sized Grids.

Hub – A Hub is the union of the following components:

  • Router
  • Distributor
  • Session Map
  • New Session Queue
  • Event Bus

By default, the server will be listening on http://localhost:4444, and that’s the URL you should point your RemoteWebDriver tests.

java -jar selenium-server-4.1.2.jar hub

Node – One or more Nodes can be started in this setup, and the server will detect the available drivers that it can use from the System PATH.

java -jar selenium-server-4.1.2.jar node --detect-drivers true

3. Distributed

On Distributed mode, each component needs to be started on its own. This setup is more suitable for large Grids.

  1. Event Bus: serves as a communication path to other Grid components in subsequent steps.
java -jar selenium-server-4.1.2.jar  event-bus

2. Session Map: responsible for mapping session IDs to the Node where the session is running.

java -jar selenium-server-4.1.2.jar sessions

3. New Session Queue: adds the new session request to a queue, then the distributor processes it.

java -jar selenium-server-4.1.2.jar sessionqueue

4. Distributor: Nodes register to it, and assigns a Node for a session request.

java -jar selenium-server-4.1.2.jar distributor --sessions http://localhost:5556 --sessionqueue http://localhost:5559 --bind-bus false

5. Router: the Grid entrypoint, in charge of redirecting requests to the right component.

java -jar selenium-server-4.1.2.jar router --sessions http://localhost:5556 --distributor http://localhost:5553 --sessionqueue http://localhost:5559

6. Nodes

java -jar selenium-server-4.1.2.jar node 

Implementation of Standalone Selenium Grid

Now, let us see how to run Selenium tests in Selenium Grid using the Standalone option.

Step 1 – Download driver.exe for the browsers

I have downloaded the driver.exe (chrome, gecko) in Downloads.

Step 2 – Download selenium-server jar

Go to Selenium Official website. Navigate to Downloads and it shows that the latest stable version of Selenium Grid is 4.3.0. Click on that and download the jar file. Make sure to keep the browsers.exe and selenium-server jar file in the same folder.

Step 3 – Execute selenium-server jar

Open a command line terminal. Use the below command to run selenium-server standalone jar files.

java -jar selenium-server-4.3.0.jar standalone --port 4446

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.

Step 4 – Add dependencies to the Maven project

Let us add the necessary dependencies to the pom.xml in the case of the Maven Project.

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="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>org.example</groupId>
  <artifactId>Selenium4Demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>Selenium4Demo</name>
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

<dependencies>
   <dependency>
     <groupId>org.seleniumhq.selenium</groupId>
     <artifactId>selenium-java</artifactId>
     <version>4.1.2</version>
  </dependency>

   <dependency>
      <groupId>io.github.bonigarcia</groupId>
      <artifactId>webdrivermanager</artifactId>
      <version>5.1.0</version>
   </dependency>
  
   <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>7.4.0</version>
      <scope>test</scope>
   </dependency>
</dependencies>

Step 5 – Create the tests

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.

HelperClass

This class contains the initialization of the Remote web driver and the closing of the web driver.

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
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;


public class HelperClass {

	protected static ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<RemoteWebDriver>();
    public static String remote_url = "http://localhost:4446";

    @BeforeMethod
    public void setDriver() throws MalformedURLException {
    	
        ChromeOptions options = new ChromeOptions();
        driver.set(new RemoteWebDriver(new URL(remote_url), options));
        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();
    }
}

Selenium4GridTest

This class contains all the tests like login test, invalid credential, verify heading of the login page, verify LinkedIn link on the login page, and verify the heading of Forgot Password page.

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import org.openqa.selenium.By;
import org.testng.annotations.Test;

public class Selenium4GridTest extends HelperClass{

	    @Test
	    public void invalidCredentials() {
	    	
	        getDriver().findElement(By.xpath("//*[@id='txtUsername']")).sendKeys("1234");
	        getDriver().findElement(By.xpath("//*[@id='txtPassword']")).sendKeys("12342");
	        getDriver().findElement(By.xpath("//*[@id='btnLogin']")).click();
	        String actualErrorMessage = getDriver().findElement(By.xpath("//*[@id='spanMessage']")).getText();
	        System.out.println("Actual ErrorMessage :" + actualErrorMessage);
	        assertEquals(actualErrorMessage,"Invalid credentials");

	    }

	    @Test
	    public void loginPageHeading() {

	        String loginText = getDriver().findElement(By.xpath("//*[@id='logInPanelHeading']")).getText();
	        System.out.println("Actual loginText :" + loginText);
	        assertEquals(loginText,"LOGIN Panel");

	    }

	    @Test
	    public void forgotPasswordPageHeading()  {

	        getDriver().findElement(By.xpath("//*[@id='forgotPasswordLink']/a")).click();
	        String forgetPasswordTitle= getDriver().findElement(By.xpath(" //*[@id='content']/div[1]/div[2]/h1")).getText();
	        System.out.println("Actual Page Title of Forgot Password Page :" + forgetPasswordTitle);
	        assertEquals(forgetPasswordTitle,"Forgot Your Password?");
	    }

	    @Test
	    public void verifyLinkedIn() {

	        Boolean linkedInIcon = getDriver().findElement(By.xpath("//*[@id='social-icons']/a[1]/img")).isEnabled();
	        System.out.println("Actual linkedIn Text :" + linkedInIcon);
	        assertTrue(linkedInIcon);

	    }
	}

Step 6 – Create a testng.xml

It is very easy to create testng.xml for Eclipse IDE. Right-click on the project -> 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 thread-count="5" name="Test">
    <classes>
      <class name="org.example.Selenium4GridTest"/>
    </classes>
  </test> <!-- Test -->
</suite> <!-- Suite -->

Step 7 – Execute the tests through the command line

To run it from the command prompt, open a command prompt and go to the project and run the following command:

mvn clean test

Step 8 – Execute the tests from testng.xml

We can right-click on testng.xml, and select Run As -> TestNG Suite.

Step 9 – Navigate to the sessions tab on the Selenium Grid UI upon running the command.

It would reflect an active session.

Step 10 – Review the test execution result

The logs can be viewed in the command prompt as shown below:

Step 11 – TestNG Report Generation

TestNG generates the test reports in test-output folder.

We are interested in index.html report. This report contains various information, like time taken by each step in the test, time taken by each test, testng.xml, and number of tests passed or failed.

Emailable-Report.html

The output reports in TestNG reporting look like below as all tests are passed:

Congratulations!! We are able to run Selenium 4 tests on the new Selenium 4 Grid. Happy Learning!!

New Features in Selenium 4

Last Updated On

HOME

Selenium is moved from version 3 to version 4 which is quite a huge step. What does this change mean? It means that a few of the old features of Selenium 3 are depreciated in Selenium 4 as well some new features are added to it also. I’m trying to explain a few of the latest updates done in Selenium 4.

Table of Contents

  1. Enhanced Selenium Grid
  2. Simplification to open a new Windows browser and Tabs
  3. Relative Locators
  4. TakeElementScreenshot
  5. New additions to the Actions Class
  6. Deprecation of Desired Capabilities
  7. Chrome Dev Tools

Selenium 3 – This is the latest version of Selenium3 available.

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.141.59</version>
</dependency>

Selenium 4 – This is the latest version of Selenium 4 Libraries that are present in the Maven Central Repository.

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.2.1</version>
</dependency>

1. Enhanced Selenium Grid

Managing Selenium Grid is now smooth and easy as there will no longer be any need to set up and start hubs and nodes separately. The grid can be deployed in 3 modes:

Standalone – Standalone is the union of all components, and to the user’s eyes, they are executed as one. A fully functional Grid of one is available after starting it in the Standalone mode. By default, the server will be listening on http://localhost:4444, and that’s the URL you should point your RemoteWebDriver tests. The server will detect the available drivers that it can use from the System PATH.

Hub and Node – It enables the classic Hub & Node(s) setup. These roles are suitable for small and middle-sized Grids

Distributed – On Distributed mode, each component needs to be started on its own. This setup is more suitable for large Grids.

Grid will now support IPv6 addresses and one can communicate with the Grid using the HTTPS protocol. In Grid 4, the configuration files used for spinning up the grid instances can be written in TOML (Tom’s Obvious, Minimal Language) which will make it easier for humans to understand.

The new Selenium Grid comes with Docker support. It also supports advanced tools like AWS, Azure, and much more, useful in the DevOps process. Now Grid has a more user-friendly UI and contains relevant information related to the session, running, capacity, etc.

2. Simplification to open a new Windows browser and Tabs

There are a number of scenarios where you would want to open a new browser (or tab) and perform a certain set of actions in the newly opened window/tab. In Selenium 3, you have to create a new Web Driver object and then switch to the new window (or tab) using its unique WindowHandle to perform subsequent actions in that window (or tab).

Selenium 4 provides a new API new Window that lets you create a new window (or tab) and automatically switch to it. Since the new window or tab is created in the same session, it avoids creating a new WebDriver object. For switching to the new tab, pass WindowType.TAB to newWindow() method and for creating a new window, pass WindowType.WINDOW to newWindow() method.

public class NewWindowDemo {

	public static void main(String[] args) {
		System.setProperty("webdriver.chrome.driver",
				"C:\\Users\\Vibha\\Software\\chromedriver_win32_93.0.4577.15\\chromedriver.exe");
		WebDriver driver = new ChromeDriver();
		driver.manage().window().maximize();

		driver.get("https://www.bing.com/");
		System.out.println("New Page - Bing is opened");

		// Opens a new window and switches to new window
		driver.switchTo().newWindow(WindowType.WINDOW);

		// Opens duckduckgo homepage in the new opened window
		driver.navigate().to("https://www.duckduckgo.com/");

		System.out.println("New Page - DuckDuckGo is opened");

		driver.quit();
	}
}

Open a new Tab in Selenium 4

public class NewTabDemo {

	public static void main(String[] args) {

		System.setProperty("webdriver.chrome.driver",
				"C:\\Users\\Vibha\\Software\\chromedriver_win32_93.0.4577.15\\chromedriver.exe");
		
		WebDriver driver = new ChromeDriver();
		driver.manage().window().maximize();

		driver.get("https://www.bing.com/");
		System.out.println("New Page - Bing is opened");

		// Opens a new window and switches to new window
		driver.switchTo().newWindow(WindowType.TAB);

		// Opens duckduckgo homepage in the new opened window
		driver.navigate().to("https://www.duckduckgo.com/");

		System.out.println("New Tab is opened with DuckDuckGo");
		driver.quit();
	}
}

3. Relative Locators

Selenium 4 brings Relative Locators which are previously called as Friendly Locators. This functionality was added to help you locate elements that are nearby other elements. The Available Relative Locators are:

above
below
toLeftOf
toRightOf
near

findElement method now accepts a new method withTagName() which returns a RelativeLocator.

import static org.openqa.selenium.support.locators.RelativeLocator.with;

a) above() – Returns the WebElement, which appears above the specified element.

WebElement passwordField_above = driver.findElement(By.id("txtPassword"));
		WebElement emailAddressField_above = driver.findElement(with(By.tagName("input")).above(passwordField_above));

b) below() – Returns the WebElement, which appears below the specified element.

WebElement emailAddress_below = driver.findElement(By.id("txtUsername"));
WebElement passwordField_below = driver.findElement(with(By.tagName("input")).below(emailAddress_below));

c) toRightOf() – Target web element which is presented to the right of a specified element.

WebElement submitButton= driver.findElement(By.id("submit"));
WebElement cancelButton= driver.findElement(with(By.tagName("button"))
                                            .toLeftOf(submitButton));

d) toRightOf() – Returns the WebElement, which appears to the right of the specified element.

WebElement cancelButton= driver.findElement(By.id("cancel"));
WebElement submitButton= driver.findElement(with(By.tagName("button")).toRightOf(cancelButton));

e) near() – Returns the WebElement, which is at most 50px away from the specified element.

WebElement emailAddressLabel= driver.findElement(By.id("lbl-email"));
WebElement emailAddressField = driver.findElement(with(By.tagName("input")).near(emailAddressLabel));

4. TakeElementScreenshot

In Selenium 3, there was a provision to capture a screenshot of the entire web page. Selenium 4 onwards, there is a new option to capture screenshots of a particular WebElement. Hence, there is no need to use third-party tools like Shutterbug, Ashot, etc. (like in Selenium 3) for capturing a screenshot of WebElement.

The newly introduced method in Selenium 4 captures the screenshot of an element for the current browsing context. The screenshot returned by the WebDriver endpoint is encoded in the Base64 format.

This is how you can capture WebElement Screenshot in Selenium 4 (for Java):

import io.github.bonigarcia.wdm.WebDriverManager;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

public class ScreenshotDemo {

	public static void main(String[] args) {
		WebDriverManager.chromedriver().setup();
		WebDriver driver = new ChromeDriver();
		driver.manage().window().maximize();
		driver.get("https://www.selenium.dev/");

		try {

			WebElement logo = driver.findElement(By.xpath("//*[@id='td-cover-block-0']/div/div/div/div/h1"));
			File source = ((TakesScreenshot) logo).getScreenshotAs(OutputType.FILE);
			FileUtils.copyFile(source, new File("./Screenshots/logo" + System.currentTimeMillis() + ".png"));
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}

		System.out.println("The Screenshot is taken and saved under Screenshots folder");
		driver.quit();
	}

}

The output of the above program is

The picture will be saved in the Screenshots folder as shown below:

Below is the image of the screenshots.

5. New additions to the Actions Class

Actions Class in Selenium provides several methods for performing a single action or a series of actions on the WebElements present in the DOM. Mouse actions (e.g., click, double click, etc.) and Keyboard actions (e.g., keyUp, keyDown, sendKeys) are the two broad categories of Actions.
For demonstration, we will post the examples demonstrated in the Action class in the Selenium blog from Selenium 3 to Selenium 4.

With Selenium 4, new methods are added to the Actions class, which replaces the classes under the org.openqa.selenium.interactions package.

  • click(WebElement) is the new method added to the Actions class and it serves as the replacement of moveToElement(onElement).click() method.

Like the method in the versions before Selenium 4, click(WebElement) is used for clicking a web element.

  • doubleClick(WebElement)

This method is added to replace moveToElement(element).doubleClick(). It will perform a double-click on an element.

import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class DoubleClick {
    public static void main(String[] args) {

        WebDriverManager.chromedriver().setup();
        WebDriver driver = new ChromeDriver();

        // Navigate to Url
        driver.get("https://demo.guru99.com/test/simple_context_menu.html");

        // Store 'doubleClickButton' button web element
        WebElement doubleClickButton = driver.findElement(By.xpath("//*[@id='authentication']/button"));
        Actions actionProvider = new Actions(driver);

        // Perform double-click action on the element
        actionProvider.doubleClick(doubleClickButton).build().perform();
        Alert alert = driver.switchTo().alert();
        System.out.println("Alert Text\n" +alert.getText());
        alert.accept();
        
        driver.close();
    }
}

The output of the above program is

  • clickAndHold(WebElement)

This method will replace the moveToElement(onElement).clickAndHold(). It is used to click on an element without releasing the click.

  • contextClick(WebElement)

This method will replace moveToElement(onElement).contextClick(). It will perform the right-click operation.

  • release()

This method (user for releasing the pressed mouse button) was initially a part of org.openqa.selenium.interactions.ButtonReleaseAction class. Now with the updated version of Selenium, it has been moved to the Actions class.

import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class clickAndHold {
    public static void main(String[] args) {

        WebDriverManager.chromedriver().setup();
        WebDriver driver = new ChromeDriver();

        // Navigate to Url
        driver.get("https://crossbrowsertesting.github.io/drag-and-drop.html");
        driver.manage().window().maximize();

        // Find element xpath which we need to drag
        WebElement from = driver.findElement(By.id("draggable"));

        // Find element xpath where we need to drop
        WebElement to = driver.findElement(By.id("droppable"));

        Actions actionProvider = new Actions(driver);

        // Perform click-and-hold action on the element
        actionProvider.clickAndHold(from).build().perform();

       // Move to drop Webelement
        actionProvider.clickAndHold(to).build().perform();

        //Release drop element
        actionProvider.release(to).build().perform();
    }
}

The output of the above program is

6. Deprecation of Desired Capabilities

In Selenium 3, desired Capabilities were primarily used in the test scripts to define the test environment (browser name, version, operating system) for execution on the Selenium Grid.

In Selenium 4, capabilities objects are replaced with Options. This means testers now need to create an Options object, set test requirements, and pass the object to the Driver constructor.

Listed below are the Options objects to be used going forward for defining browser-specific capabilities:

Firefox – FirefoxOptions
Chrome – ChromeOptions
Internet Explorer (IE) – InternetExplorerOptions
Microsoft Edge – EdgeOptions
Safari – SafariOptions

Below is an example of Options

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import io.github.bonigarcia.wdm.WebDriverManager;

public class ChromeOptionsHeadless {

	public static void main(String[] args) {

		WebDriverManager.chromedriver().setup();

		ChromeOptions chromeOptions = new ChromeOptions();
		chromeOptions.setBrowserVersion("100");
		chromeOptions.setPlatformName("Windows 10");

		WebDriver driver = new ChromeDriver(chromeOptions);

		driver.get("https://duckduckgo.com/");
		System.out.println("Title of Page :" + driver.getTitle());

		// Close the driver
		driver.close();

	}
}

The output of the above program is

Similarly, we can create the action class for other browsers like Firefox.

FirefoxOptions options = new FirefoxOptions();
 
// Create an object of WebDriver class and pass the Firefox Options object as an argument
WebDriver driver = new FirefoxDriver(options);

7. Chrome Dev Tools

In the new version of Selenium, they have made some internal changes in the API. Earlier in Selenium 3, the Chrome driver extends directly to the Remote Web Driver class. But now in Selenium 4, the Chrome driver class extends to Chromium Driver. Chromium Driver class has some predefined methods to access the dev tool.

Note: Chromium Driver extends the Remote Web driver class.

By using the API, we can perform the following operations:

  • Enable Network Offline
  • Enable Network Online
  • Get Console Logs
  • Load Insure Web Site