Page Object Model with Selenium, Cucumber and JUnit

HOME

In this tutorial, I’ll create a BDD Framework for the testing of web applications using the Page Object Model. This framework consists of

  1. Cucumber Java – 7.6.0
  2. Cucumber JUnit4 – 7.6.0
  3. Java 11
  4. Maven – 3.8.6
  5. Selenium – 4.3.0
  6. JUnit – 4.13.2

What Is Page Object Model (POM)?

The Page Object model is an object design pattern in Selenium, where web pages are represented as classes, the various elements on the page are defined as variables in the class and all possible user interactions can then be implemented as methods in the class.

What is Cucumber?

Cucumber is one such open source tool, which supports Behavior Driven Development(BDD). In simple words, Cucumber can be defined as a testing framework, driven by plain English. It serves as documentation, automated tests, and development aid – all in one.

Steps to setup Cucumber Test Automation Framework using Page Object Model

  1. Download and Install Java on the system
  2. Download and setup Eclipse IDE on the system
  3. Setup Maven on System
  4. Install Cucumber Eclipse Plugin (Only for Eclipse IDE)
  5. Create a new Maven Project
  6. Create a source folder – src/test/resources to create test scenarios in the Feature file
  7. Add Selenium, Cucumber and JUnit4 dependencies to the project
  8. Add Maven Compiler Plugin and Surefire Plugin
  9. Create a feature file under src/test/resources
  10. Create the classes for locators, actions and utilities in src/main/java
  11. Create a Java Class called Definition where we will create the Test Code related to Given, When, Then of Feature file in src/test/java.
  12. Create a Hook class to contain the initialization and closing of browser in src/test/java
  13. Create a Cucumber Runner class in the src/test/java directory
  14. Run the tests from JUnit
  15. Run the tests from Command Line
  16. Cucumber Report Generation

Project Structure

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 Maven Project

To create a new Maven project, go to the File -> New Project-> Maven-> Maven project-> Next -> Enter Group ID & Artifact ID -> Finish.

Click here to know How to create a Maven project

Step 6 – Create source folder src/test/resources to create test scenarios in the Feature file

A new Maven Project is created with 2 folders – src/main/java and src/test/java. To create test scenarios, we need a new source folder called – src/test/resources. To create this folder, right-click on your maven project ->select New ->Java and then Source Folder.

Mention the source folder name as src/test/resources and click the Next button. This will create a source folder under your new Maven project as shown in the below image.

Step 7 – Add Selenium, JUnit4, and Cucumber dependencies to the project

Add below mentioned Selenium, JUnit4, 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.

    <properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<cucumber.version>7.6.0</cucumber.version>
		<selenium.version>4.3.0</selenium.version>
		<webdrivermanager.version>5.2.1</webdrivermanager.version>
		<junit.version>4.13.2</junit.version>
		<apache.common.version>2.4</apache.common.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>

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

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

	</dependencies>

Step 8 – Add Maven Compiler Plugin and Surefire Plugin

The compiler plugin is used to compile the source code of a Maven project. This plugin has two goals, which are already bound to specific phases of the default lifecycle:

  • compile – compile main source files
  • testCompile – compile test source files
<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>
					<target>${maven.compiler.target.version}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>${maven.surefire.plugin.version}</version>
				<configuration>
                <includes>
                    <include>**/*Tests.java</include>
                </includes>
				</configuration>
			</plugin>
		</plugins>

The complete POM.xml looks like as shown below

<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>com.example</groupId>
	<artifactId>POM_CucumberJunitDemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>


	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<cucumber.version>7.6.0</cucumber.version>
		<selenium.version>4.3.0</selenium.version>
		<webdrivermanager.version>5.2.1</webdrivermanager.version>
		<junit.version>4.13.2</junit.version>
		<apache.common.version>2.4</apache.common.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>

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

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

	</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>
					<target>${maven.compiler.target.version}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>${maven.surefire.plugin.version}</version>
				<configuration>
                <includes>
                    <include>**/*Tests.java</include>
                </includes>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>
Step 9 – 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 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 Test Scenarios in the feature file. I have failed one test scenario intentionally – @MissingUsername.

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 Outline: Login with blank username
     
    When User enters username as " " and password as "admin123"
    Then User should be able to see a message "Required1" below Username
      

Step 10 – Create the classes for locators, actions and utilities in src/main/java

Create a Java Class for each page where define WebElements as variables using Annotation @FindBy . Create another Java class that contain methods for actions performed on WebElements. Here, I’m going to create 2 classes for locators – LoginPageLocators and HomePageLocators.java as well as 2 classes for actions – LoginPageActions and HomePageActions

Locator class contains WebElements which are identified by @FindBy annotation as shown below:-

@FindBy(name = "txtUsername")
WebElement userName;

Action class contain methods for the action to be performed on the web elements identified in locator class as shown below:-

public void login(String strUserName, String strPassword) {
 
           // Fill user name
           this.setUserName(strUserName);
 
           // Fill password
           this.setPassword(strPassword);
 
           // Click Login button
           this.clickLogin();
     }
}

The initElements is a static method of PageFactory class that is used to initialize all the web elements located by @FindBy annotation. Only after the WebElements are initialized, they can be used in the methods to perform actions.

public Login(WebDriver driver) {
           this.driver = driver;
           // This initElements method will create all WebElements
           PageFactory.initElements(driver, this);
     }

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[1]/div/span")
    public WebElement missingUsernameErrorMessage;
    
    @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;
       
}

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 – LoginPageActionsHomePageActions 

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);
	}
 
	// 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 error message when username is blank
    public String getMissingUsernameText() {
        return loginPageLocators.missingUsernameErrorMessage.getText();
    }
      
    // Get the Error Message
    public String getErrorMessage() {
        return loginPageLocators.errorMessage.getText();
    }
 
    public void login(String strUserName, String strPassword) {
 
        // Fill user name
        this.setUserName(strUserName);
 
        // Fill password
        this.setPassword(strPassword);
 
        // Click Login button
        this.clickLogin();
 
    }
}

HomePageActions

import org.openqa.selenium.support.PageFactory;

import com.example.locators.HomePageLocators;
import com.example.utils.HelperClass;

public class HomePageActions {

	HomePageLocators homePageLocators = null;
    
    public HomePageActions() {
          
        this.homePageLocators = new HomePageLocators();
  
        PageFactory.initElements(HelperClass.getDriver(),homePageLocators);
    }
  
   
    // Get the User name from Home Page
    public String getHomePageText() {
        return homePageLocators.homePageUserName.getText();
    }
  
}

 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, within 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 11 – Create a Java Class called Definition where we will create the Test Code related to Given, When, Then of Feature file in src/test/java

Now, we need to create the Step Definition of Feature File – LoginPageDefinitions.java.

import org.junit.Assert;

import com.example.actions.ForgotPasswordActions;
import com.example.actions.HomePageActions;
import com.example.actions.LoginPageActions;
import com.example.utils.HelperClass;

import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;

public class LoginPageDefinitions {

	LoginPageActions objLogin = new LoginPageActions();
    HomePageActions objHomePage = new HomePageActions();
   
    @Given("User is on HRMLogin page {string}")
    public void loginTest(String url) {
    	
    	HelperClass.openPage(url);
 
    }
 
    @When("User enters username as {string} and password as {string}")
    public void goToHomePage(String userName, String passWord) {
 
        // login to application
        objLogin.login(userName, passWord);
 
        // go the next page
        
    }
    
    @Then("User should be able to login sucessfully and new page open")
    public void verifyLogin() {
 
        // Verify home page
        Assert.assertTrue(objHomePage.getHomePageText().contains("Employee Information"));
 
    }
    
    @Then("User should be able to see error message {string}")
    public void verifyErrorMessage(String expectedErrorMessage) {
 
        // Verify home page
    	Assert.assertEquals(objLogin.getErrorMessage(),expectedErrorMessage);
 
    }
    
    @Then("User should be able to see a message {string} below Username")
    public void verifyMissingUsernameMessage(String message) {
    	
    	Assert.assertEquals(objLogin.getMissingUsernameText(),message);
    }
      
}

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

Create the hook class that contains the Before and After hook to initialize the web browser and close the web browser. I have added the code to take the screenshot of the failed scenario in @After Hook.

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 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 13 – Create a JUnit Cucumber Runner class to execute the test scenarios in the src/test/java directory

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",
                 plugin = {})
   
public class CucumberRunnerTests {
   
}

Note:- The name of the Runner class should end with Test otherwise we can’t run the tests using Command Line.

Step 14 – Run the tests from JUnit

You can execute the test script by right-clicking on TestRunner class -> Run As JUnit.

Step 15 – 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.

mvn clean test
Step 16 – 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 Cucumber Service.

In the above example as we can see, one of the test has failed. So, when a test fails, we have written the code to take the 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.

That’s it! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s