In the previous tutorials, we discussed BDD(Behaviour Driven Development) and Gherkin. Cucumberis one such open-source tool, which supports Behaviour 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.
In this tutorial, we will set up Cucumber with Eclipse.
Java is a robust programming language. Java is a general-purpose programming language that is concurrent; class-based and object-oriented language. Java follows the concept of “write once and run anywhere (WORA)” which means that compiled Java code can be run on all different platforms that support Java without the need for recompilation. Cucumber supports the Java platform for execution. Click here to know How to install Java.
2. Download and Start Eclipse
Eclipse is an Integrated Development Environment (IDE). It contains a base workspace and an extensible plug-in system for customizing the environment. To download Eclipse, please refer to this tutorial – How to install Eclipse.
3. Maven – How to install Maven on Windows
Apache Maven is a software project management and comprehension tool. It uses the concept of a project object model (POM), Maven can manage a project’s build, reporting, and documentation from a central piece of information. MAVEN helps us in creating the project structure and managing and downloading the dependencies. We need to define the required dependencies in pom.xml. To install Maven on Windows, please refer to this tutorial –How to install Maven.
4. Install Cucumber Eclipse Plugin
The Cucumber plugin is an Eclipse plugin that allows eclipse to understand the Gherkin syntax. Cucumber Eclipse Plugin highlights the keywords present in Feature File. To install Cucumber Eclipse Plugin, please refer to this tutorial – How to install Cucumber Eclipse Plugin
This will indicate Maven, which Cucumber files are to be downloaded from the central repository to the local repository. Create one more dependency tag.
This will indicate Maven, which Cucumber JUnit files are to download from the central repository to the local repository. Create one more dependency tag.
The Page Object model is an object design pattern in Selenium. Web pages are represented as classes. The various elements on the page are defined as variables in the class. All possible user interactions can then be implemented as methods in the class.
Advantages of Page Object Model
Simplifies the process of adapting to changes in the UI. When page elements change due to a UI update, modifications are isolated to the page object class.
Page objects contain methods that represent user actions on a web page. These methods make the tests more readable and understandable.
Common code related to web pages is stored in page classes. This code is reused in tests. This approach results in the reduction of the code.
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.
Dependency List
Cucumber Java – 7.6.0
Cucumber JUnit4 – 7.6.0
Java 11
Maven – 3.8.6
Selenium – 4.3.0
JUnit – 4.13.2
Project Structure
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 write the feature files that contain Feature, Scenario, Given, When, Then, And, and But. They also include 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.
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
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:
Step 9 – Create a feature file in the src/test/resourcesdirectory
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 contains 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
The Locator class contains WebElements which are identified by @FindBy annotation as shown below:-
Action class contains methods for the action to be performed on the web elements identified in the 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 the 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 – LoginPageActions, 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);
}
// 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. We are also initializing the web driver wait and defining the timeouts. A private constructor of the class is created. It will declare the web driver. 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 in src/test/java
Create a Java Class called Definition where we will create the Test Code related to Given, When, Then of the Feature file 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.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 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 classin 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. Then 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 the Command Line
Run the below command in the command prompt to run the tests and to get the test execution report.
mvn clean test
The output of the above program is
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 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.
That’s it! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
Cucumber is a BDD Tool, and Selenium WebDriver is used for the automation of web applications. Imagine we need to build a test framework. This framework can be used by businesses to understand the test scenarios. It can also test the web application. This can be achieved by integrating Cucumber with Selenium. I’m going to use TestNG as the Test Automation tool for assertions. In the previous tutorial, I used Cucumber with Page Object Model. To know more about this, please refer to this tutorial – Page Object Model with Selenium, Cucumber, and TestNG.
In this tutorial, I’ll create a BDD Framework for the testing of web applications. I will use Cucumber, Selenium WebDriver, Maven and TestNG.
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, which is needed to write Java code. 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. It is a very tedious and cumbersome process to add each dependency manually. So, to overcome this problem, we use a build management tool. Maven is a build management tool that is used to define project structure, dependencies, build, and test management. Click here to know How to install Maven.
The Cucumber Eclipse plugin is a plugin that allows eclipse to understand the Gherkin syntax. The Cucumber Eclipse Plugin highlights the keywords present in Feature File. Click here to know more – How to install Cucumber Eclipse Plugin
Group Id – com.example Artifact Id – Cucumber_TestNG_Demo Version – 0.0.1-SNAPSHOT Package – com. example. Cucumber_TestNG_Demo
Step 7 – Create source folder src/test/resources to create test scenarios in Feature file
When a new Maven Project is created, it has 2 folders – src/main/java and src/test/java as shown below image. 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.
Step 8 – Add Selenium, TestNG, and Cucumber dependencies to the project
Add the below-mentioned Selenium, TestNG, and Cucumber dependencies to the project.
Step 9 – 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:
If you don’t add a compiler plugin to the POM.xml, the build will fail. This happens when you try to run the tests through Maven. Then the build will fail with the below message.
Step 10 – Create a feature file under src/test/resources/features
It is recommended to create a features folder in the src/test/resources directory. Create all the feature files in this features folder. Feature file should be saved as an extension of .feature. The test scenarios in the Feature file are written in Gherkins language. Add the test scenarios in this feature file. I have added sample test scenarios.
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 |
Step 11 – Create the step definition class in src/test/java
Create the step definition class corresponding to the feature file to test the scenarios in the src/test/java directory. The StepDefinition files should be created in this definitionsdirectory within the folder called definitions.
Below is the step definition of the LoginPage feature file.
package com.example.definitions;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.testng.Assert;
import java.time.Duration;
public class LoginPageDefinitions {
private static WebDriver driver;
public final static int TIMEOUT = 5;
@Before
public void setUp() {
ChromeOptions options = new ChromeOptions();
options.addArguments("--start-maximized");
driver = new ChromeDriver(options);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
}
@Given("User is on HRMLogin page {string}")
public void loginTest(String url) {
driver.get(url);
}
@When("User enters username as {string} and password as {string}")
public void goToHomePage(String userName, String passWord) {
// login to application
driver.findElement(By.name("username")).sendKeys(userName);
driver.findElement(By.name("password")).sendKeys(passWord);
driver.findElement(By.xpath("//*[@class='oxd-form']/div[3]/button")).submit();
}
@Then("User should be able to login successfully and new page open")
public void verifyLogin() {
String homePageHeading = driver.findElement(By.xpath("//*[@class='oxd-topbar-header-breadcrumb']/h6")).getText();
//Verify new page - HomePage
Assert.assertEquals(homePageHeading, "Dashboard");
}
@Then("User should be able to see error message {string}")
public void verifyErrorMessage(String expectedErrorMessage) {
String actualErrorMessage = driver.findElement(By.xpath("//*[@class='orangehrm-login-error']/div[1]/div[1]/p")).getText();
// Verify Error Message
Assert.assertEquals(actualErrorMessage, expectedErrorMessage);
}
@After
public void teardown() {
driver.quit();
}
}
assertThat() and containsString are imported from package:-
Step 12 – Create a TestNG Cucumber Runner classin src/test/java
We need to create a class called Runner class to run the tests. This class will use the TestNG annotation @RunWith(), which tells TestNG what is the test runner class. TestRunner should be created under src/test/java within the folder called runner.
Below is the code for the Cucumber Runner Class.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "", features = {"src/test/resources/features/LoginPage.feature"}, glue = {"com.example.definitions"},
plugin = {})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
AbstractTestNGCucumberTests – Runs each cucumber scenario found in the features as a separate test.
Step 13 – Test Execution through TestNG
Go to the Runner class and right-click “Run As TestNG Test”. The tests will run as TestNG tests. This is for Eclipse.
In case you are using IntelliJ, then select “Run CucumberRunner Tests“.
This is what the execution console will look like in Eclipse.
Step 14 – Run the tests from TestNG.xml
Create a TestNG.xml as shown below and run the tests as TestNG.
Below is an example of testng.xml.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Cucumber with TestNG Test">
<classes>
<class name="com.example.runner.CucumberRunnerTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Step 15 – Run the tests from the Command Line
Run the below command in the command prompt to run the tests and to get the test execution report.
mvn clean test
The execution screen looks like something as shown below.
Step 16 – Cucumber Report Generation
Add cucumber.properties under src/test/resources and add the below instructions in the file.
cucumber.publish.enabled=true
Below is the image of the Cucumber Report generated using the Cucumber Service.
Step 17 – TestNG Report Generation
TestNG generates various types of reports under the test-output or target folder like emailable-report.html, index.html, testng-results.xml.
We are interested in the ‘emailable-report.html’ report. Open “emailable-report.html“, as this is an HTML report, and open it with the browser. The below image shows emailable-report.html.
emailable-report.html
Index.html
TestNG also produces “index.html” report, and it resides under the test-output folder. The below image shows the index.html report.
CI/CD Integration:
Integrating BDD tests with Continuous Integration and Continuous Deployment (CICD) pipelines helps automate the testing process. Tools like Jenkins, GitLab CI, or GitHub Actions can be configured easily. They run BDD tests automatically during the build process. Here’s how:
Configure the CICD Pipeline:Set up the pipeline to build the project, run tests, and create reports.
Run Tests: Ensure the pipeline runs your BDD tests using Maven or Gradle.
Publish Reports: Configure the pipeline to show test results, so you can easily see if there are any issues.
Troubleshooting Tips:
1. Maven Dependencies Not Resolved: The dependencies for Cucumber, Selenium, or TestNG are not resolving in your project. Make sure your pom.xml is correctly configured and the Maven repository has the latest dependencies. Run mvn clean install to force update dependencies. Also, check if you have internet access and the correct settings in your Maven configuration.
2. TestNG XML File Setup: TestNG tests are not running as expected, or the test results are incorrect. Double-check your `testng.xml` configuration to ensure that the correct test classes, packages, or methods are included. Make sure annotations like `@Test` are correctly used in your test classes.
3. Cucumber Features Not Recognized: Cucumber feature files aren’t recognized by your test runner. Verify your `CucumberOptions` in the test runner class points to the correct `features` path and `glue` path for step definitions. Make sure your feature files have the correct `.feature` extension and contain valid Gherkin syntax.
We may quickly validate the steps in your feature using Dry Run in Cucumber, without having to run the code inside the appropriate step definitions, to ensure that every Step has its corresponding Step Definition present in the Step Definition file. A cucumber dry run is used to confirm the compilation faults and compile the Step Definition and Feature files. It is a way to validate the correctness of the Gherkin syntax in the feature files and the mapping of steps defined in the step definitions without executing the actual application code.
The Dry Run option can either be set as true or false. By default, it is false.
Dry Run as true
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(plugin = {"pretty"},
features = "src/test/resources/Features",
glue = "org.example.stepdefinitions",
monochrome=true,
dryRun = true)
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
Below is the 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
In the below snippet of code, I have created the stepdefinitions for the Given and When statements and no stepdefinition for the Then statement.
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import java.time.Duration;
public class LoginPageDefinitions {
private static WebDriver driver;
public final static int TIMEOUT = 5;
@Before
public void setUp() {
WebDriverManager.chromedriver().setup();
ChromeOptions options = new ChromeOptions();
options.addArguments("--start-maximized");
driver = new ChromeDriver(options);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
}
@Given("User is on HRMLogin page {string}")
public void loginTest(String url) {
driver.get(url);
}
@When("User enters username as {string} and password as {string}")
public void goToHomePage(String userName, String passWord) {
// login to application
driver.findElement(By.name("username")).sendKeys(userName);
driver.findElement(By.name("password")).sendKeys(passWord);
driver.findElement(By.xpath("//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[3]/button")).submit();
// go the next page
}
@After
public void teardown() {
driver.quit();
}
}
Execute the feature file by right-clicking on the Runner class and selecting – Run As TestNG Test.
In the case of the dry run as true, none of the Steps is executed, but Cucumber has only made sure that every Step has the interconnected method available in the Step Definition file. Take a look at the time duration at the end of every Step. It is extremely less.
The output of the above program is
Dry Run as false
In the below case, dryRun is false.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(plugin = {"pretty"},
features = "src/test/resources/Features",
glue = "org.example.stepdefinitions",
monochrome=true,
dryRun = false)
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
The output of the above program is
In case of dry run as false, the steps 1 and 2 will be executed. The browser will open and all statements inside the step definitions method will execute when set to false. The time taken to complete this test is 0.235 sec.
So, you can set dry run to true to quickly check if any of the step definition is not implemented.
In order to eliminate unreadable characters from the console output during execution, we set the monochrome option to true inside the @CucucmberOptions annotation. You can set this option to true or false. By default, it is set to False.
Monochrome as False in Cucumber
Monochrome is mentioned in the Runner class.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "", features = "src/test/resources/Features", glue = "org.example.stepdefinitions", monochrome=false
,plugin = {"pretty"})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
The output of the above program is
Monochrome as True in Cucumber
When monochrome value set to true, It will make console output for the Cucumber test much more readable and remove any unreadable character.
package org.example.runner;
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "", features = "src/test/resources/Features", glue = "org.example.stepdefinitions", monochrome=true
,plugin = {"pretty"})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
The output of the above program is
Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
In this tutorial, I will explain the use of Hooks in Cucumber.
What is a Hook in Cucumber?
Hooks are blocks of code that can run at various points in the Cucumber execution cycle. They are typically used for setup and teardown of the environment before and after each scenario. These hooks do not impact the scenarios or steps for which they are used. We can declare hooks in any class.
Why do we use Hooks?
There are scenarios where we have to perform some prerequisite steps before executing the test scenarios, like initiating a WebDriver, setting up database connection, setting up Test Data, and setting up browser cookies.
Similarly, there are some conditions that need to be done after completing the execution of test scenarios like killing the web driver, closing database connections, clearing the test data, clearing browser cookies, and so on.
Scenario hooks
Scenario hooks run for every scenario. There are 2 types of Scenario Hooks – @After and @Before
Before Before hooks run before the first step of each scenario.
Syntax:
@Before
public void setup() {
System.out.println("------------------Before Executing-------------------------");
WebDriverManager.chromedriver().setup();
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--start-maximized");
driver = new ChromeDriver(chromeOptions);
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
}
After After hooks run after the last step of each scenario, even when the step result is failed, undefined, pending, or skipped.
Syntax:
@After
public void close() {
driver.close();
System.out.println("---------------After Executing---------------------------");
}
Here is an example of Hooks – @Before and @After in a Cucumber program.
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
public class LoginPageDefinitions {
WebDriver driver;
@Before
public void setup() {
System.out.println("------------------Before Executing-------------------------");
WebDriverManager.chromedriver().setup();
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--start-maximized");
driver = new ChromeDriver(chromeOptions);
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
}
@Given("User is on HRMLogin page")
public void userOnHomePage() {
System.out.println("Open Website");
driver.get("https://opensource-demo.orangehrmlive.com/");
}
@When("User enters username as {string}")
public void entersUsername(String userName){
System.out.println("Enter username");
driver.findElement(By.name("username")).sendKeys(userName);
}
@When("User enters password as {string}")
public void entersPassword(String passWord) {
System.out.println("Enter passWord");
driver.findElement(By.name("password")).sendKeys(passWord);
driver.findElement(By.xpath("//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[3]/button")).submit();
}
@Then("User should be able to login successfully")
public void successfulLogin() throws InterruptedException {
String newPageText = driver.findElement(By.xpath("//span[@class='oxd-topbar-header-breadcrumb']/h6")).getText();
System.out.println("newPageText:" + newPageText);
assertThat(newPageText, containsString("Dashboard"));
}
@After
public void close() {
driver.quit();
System.out.println("--------------------After Executing-----------------------");
}
}
The output of the above program is
At the start of execution, @Before annotation is setting up the web driver to execute the test.
After setting up the web driver, the Given, When, and Then statements will be executed.
Now, at last, @After hook will close the web driver.
Step hooks
Step hooks are invoked before and after a step. The hooks have ‘invoke around’ semantics. This means that if a BeforeStep hook is executed, the AfterStep hooks will also be executed regardless of the result of the step.
@BeforeStep – As the name suggests, it is executed before the execution of each step.
Syntax:
@BeforeStep
public void beforeStepTest() {
System.out.println("--------------BeforeStep Executing---------------");
}
@AfterStep – As the name suggests, it is executed after the successful execution of each step. If a step does not pass, the following step and its hooks will be skipped.
Syntax:
@AfterStep
public void afterStepTest() {
System.out.println("--------------------AfterStep Executing---------------------");
}
The complete program is shown below:
import io.cucumber.java.After;
import io.cucumber.java.AfterStep;
import io.cucumber.java.Before;
import io.cucumber.java.BeforeStep;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
public class LoginPageDefinitions {
WebDriver driver;
@Before
public void setup() {
System.out.println("------------------Before Executing-------------------------");
WebDriverManager.chromedriver().setup();
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--start-maximized");
driver = new ChromeDriver(chromeOptions);
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
}
@BeforeStep
public void beforeStepTest() {
System.out.println("--------------BeforeStep Executing---------------");
}
@Given("User is on HRMLogin page")
public void userOnHomePage() {
System.out.println("Open Website");
driver.get("https://opensource-demo.orangehrmlive.com/");
}
@When("User enters username as {string}")
public void entersUsername(String userName){
System.out.println("Enter username");
driver.findElement(By.name("username")).sendKeys(userName);
}
@When("User enters password as {string}")
public void entersPassword(String passWord) {
System.out.println("Enter passWord");
driver.findElement(By.name("password")).sendKeys(passWord);
driver.findElement(By.xpath("//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[3]/button")).submit();
}
@Then("User should be able to login successfully")
public void successfulLogin() throws InterruptedException {
String newPageText = driver.findElement(By.xpath("//span[@class='oxd-topbar-header-breadcrumb']/h6")).getText();
System.out.println("newPageText:" + newPageText);
assertThat(newPageText, containsString("Dashboard"));
}
@AfterStep
public void afterStepTest() {
System.out.println("--------------------AfterStep Executing---------------------");
}
@After
public void close() {
driver.quit();
System.out.println("--------------------After Executing-----------------------");
}
}
The output of the above program is
At the start of execution, @Before annotation is setting up the web driver to execute the test.
After setting up the web driver, @BeforeStep is executed before executing the first step.
After the execution of the first step (Given), @AfterStep is executed.
Here, it can be seen that there are 4 steps and for each step, there is a combination of @BeforeStep and @AfterStep.
That’s it! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
BDD is a set of practices that helps to reduce the rework caused by misunderstanding or vague requirements, narrow the communication gaps between the development team, testing team, and customers, and promote continuous communication among them. Cucumber is one such open-source tool, which supports Behaviour 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 a development aid – all in one.
Cucumber Introduction, Installation, and Configuration
The previous tutorial explained the Integration of Cucumber with Selenium and TestNG. Sometimes, inconsistent test results are common as a result of an unstable environment like network issue or Database down and soon. A few tests may fail for no obvious reason and then rerun successfully. We are sometimes required to run only failed test cases after bug fixes to verify fixes quickly. We will learn how to rerun failed test cases in the Cucumber with TestNG project in this post.
Cucumber provides a rerun plugin option in the Runner class. This option generates a file. The file contains information about the failed tests.
Now, let us add a rerun plugin to the Cucumber Runner class. Here, we are creating a failedrerun.txt file that contains the information about the failed test. This file will be created under the target folder.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "", features = "src/test/resources/features/LoginPage.feature",
glue = "com.example.definitions",
plugin = {
"pretty",
"rerun:target/rerun.txt" // Saves paths of failed scenarios
}
)
public class RunnerTests extends AbstractTestNGCucumberTests {
}
Create a Second Runner Class
The next step is to run failed test scenarios existing in the text file. We need to create a class similar to our runner class. This class will contain the location of the file that we want to execute. It will rerun our failed scenarios. In the ‘features’ variable, you need to mention the failedrerun.txt file, and don’t forget that you must mention the ‘@’ symbol before the file path.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "",
features = "@target/rerun.txt",
glue = "com.example.definitions",
plugin = {
"pretty"
}
)
public class RunnerTestsFailed extends AbstractTestNGCucumberTests {
}
Mention both Test Runner details in the testng.xml.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Cucumber with TestNG Test">
<classes>
<class name="com.example.runner.RunnerTests"/>
<class name="com.example.runner.RunnerTestsFailed"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Run the tests using the below-mentioned command
mvn clean test
After running the tests from the command line, first, all the tests will be executed. If any test fails, a failedrerun.txt file will be generated that includes the details about the failed tests.
In the below screenshot, we can see that a scenario starting at line 25 has failed.
The first round of execution ends. Then, Cucumber Runner goes to the second runner. It runs the failed tests that are mentioned in failedrerun.txt.
We can see that 2 separate reports are generated here.
The first Cucumber Report shows that out of 5 tests, 1 test failed.
The second Cucumber Report shows that the one failed test is rerun again, and it again failed.
Cucumber is not an API automation tool, but it works well with other API automation tools.
There are 2 most commonly used Automation Tools for JVM to test API – Rest-Assured and Karate. In this tutorial, I will use RestAssured with Cucumber and JUnit4 for API Testing.
REST Assured is a Java library that provides a domain-specific language (DSL) for writing powerful, maintainable tests for RESTful APIs. REST Assured can be used easily in combination with existing unit testing frameworks, such as JUnit and TestNG. Rest assured, no matter how complex the JSON structures are, Rest Assured has methods to retrieve data from almost every part of the request and response.
What is Cucumber?
Cucumber is one such open-source tool, which supports Behaviour 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.
Each scenario is a set of steps that the Cucumber must complete. Cucumber validates the software’s compliance with the specification and generates a report indicating success or failure for each scenario.
The cucumber must adhere to some basic syntax rules known as Gherkin in order to comprehend the scenarios.
In this tutorial, I will explain creating a framework for the testing of Rest API in Cucumber BDD.
This framework consists of:
Cucumber – 7.18.0
Java 17
JUnit – 4.13.2
Maven – 3.9.6
Rest Assured – 5.4.0
Maven Compiler – 3.13.0
Project Structure
Implementation Steps
Step 1 – Download and Install Java
Cucumber and Rest-Assured 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 several dependencies to the project. Click here to know How to install Maven.
Step 4 – Create a new Maven Project
File -> New Project-> Maven-> Maven project-> Next -> Enter Group ID & Artifact ID -> Finish
Step 5 – Install the Cucumber Eclipse plugin for the Eclipse project(Eclipse Only)
The Cucumber plugin is an Eclipse plugin that allows eclipse to understand the Gherkin syntax. Cucumber Eclipse Plugin highlights the keywords present in Feature File. To install Cucumber Eclipse Plugin, please refer to this tutorial – How to install Cucumber Eclipse Plugin.
Step 6 – Create source folder src/test/resources
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 Rest-Assured and Cucumber dependencies to the project
You should place rest-assured before the JUnit dependency declaration in your pom.xml / build.gradle to make sure that the correct version of Hamcrest is used. REST Assured includes JsonPath and XmlPath as transitive dependencies.
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:
Step 9 – Create a feature file under src/test/resources
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 a Test Scenario where we are using the GET method to get the information from the API.
Feature: Validation of get method
@GetUserDetails
Scenario Outline: Send a valid Request to get user details
Given I send a request to the URL to get user details
Then the response will return status <statusCode> and id <id> and email "<employee_email>" and first name "<employee_firstname>" and last name "<employee_lastname>"
Examples:
| statusCode | id | employee_email | employee_firstname | employee_lastname |
| 200 | 2 | janet.weaver@reqres.in | Janet | Weaver |
Step 10 – Create the Step Definition class or Glue Code for the Test Scenario
StepDefinition acts as an intermediate to your runner and feature file. It stores the mapping between each step of the scenario in the Feature file. So when you run the scenario, it will scan the step definition file to check the matched glue or test code.
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
public class APIDemoDefinitions {
private ValidatableResponse validatableResponse;
private String endpoint = "https://reqres.in/api/users/2";
@Given("I send a request to the URL to get user details")
public void sendRequest(){
validatableResponse = given().contentType(ContentType.JSON)
.when().get(endpoint).then();
System.out.println("Response :"+validatableResponse.extract().asPrettyString());
}
@Then("the response will return status {int} and id {int} and email {string} and first name {string} and last name {string}")
public void verifyStatus(int expectedStatusCode, int expectedId, String expectedEmail, String expectedFirstName, String expectedLastName){
validatableResponse.assertThat().statusCode(expectedStatusCode);
validatableResponse.assertThat().body("data.id",equalTo(expectedId));
validatableResponse.assertThat().body("data.email",equalTo(expectedEmail));
validatableResponse.assertThat().body("data.first_name",equalTo(expectedFirstName));
validatableResponse.assertThat().body("data.last_name",equalTo(expectedLastName));
}
}
In order to use REST assured effectively it’s recommended to statically import methods from the following classes:
There is another way to perform these assertions. We can use multiple body assertions together.
@Then("the response will return status {int} and id {int} and email {string} and first name {string} and last name {string}")
public void verifyStatus(int expectedStatusCode, int expectedId, String expectedEmail, String expectedFirstName, String expectedLastName){
validatableResponse.assertThat().statusCode(expectedStatusCode).body("data.id",equalTo(expectedId)).and()
.body("data.email",equalTo(expectedEmail)).body("data.first_name",equalTo(expectedFirstName))
.body("data.last_name",equalTo(expectedLastName));
Step 11 – Create a JUnit Cucumber Runner class
A runner will help us to run the feature file and act as an interlink between the feature file and StepDefinition Class.
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
@RunWith(Cucumber.class)
@CucumberOptions(features= {"src/test/resources"}, glue= {"com.example.stepdefinitions"})
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 12 – Run the tests from JUnit
You can execute the test script by right-clicking on TestRunner class -> Run As JUnit (Eclipse).
You can execute the test script by right-clicking on TestRunner class -> Run CucumberRunnerTests (IntelliJ).
Step 13 – Run the tests from the Command Line
Run the below command in the command prompt to run the tests and to get the test execution report.
mvn clean test
The output of the above program is
Step 14 – 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
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
Cucumber tests are grouped into features. A Feature file is one in which we store a description of the features and scenarios to be tested. It is used to provide a high-level description of test scenarios and group-related scenarios. A Feature File is an entry point to the Cucumber tests.
The first keyword in the Feature file is the Feature keyword, followed by: and short text that describes the feature.
You can add free-form text underneath the Feature to add more description.
These description lines are ignored by Cucumber at runtime, but are available for reporting (They are included by default in HTML reports).
The name and the optional description have no special meaning to Cucumber. Their purpose is to provide a place for you to document important aspects of the feature, such as a brief explanation and a list of business rules (general acceptance criteria).
The free format description for Feature ends when you start a line with the keyword Example or Scenario Outline (or their alias keywords).
You can place tags above Feature to group related features, independent of your file and directory structure.
A simple feature file consists of the following keywords/parts −
FeatureFile Name – Name of the feature under test. For example, here is LoginPage.feature
Feature − Describe the feature under test, like here “Login to HRM Application”
Scenario− What is the test scenario we want to test
Given − Prerequisite before the test steps get executed.
When − Specific condition that should match to execute the next step.
Then − What should happen if the condition mentioned in WHEN is satisfied
And/But – If we have several Given’s, When’s, or Then’s, then we can use And /But
Steps to create a Feature file
Step 1 – Create a new Maven project.
Step 2 – Add cucumber-java dependency to the POM.xml.
Step 3 – It is suggested to create the Feature files in the src/test/resources source folder. By default, this folder is not present. So, first, create a Source Folder named src/test/resources. Right-click on the project, and select New → Source Folder.
Provide the name to the Source Folder and click on the Finish button.
This creates a new Source Folder.
Step 4 –We want all our Feature files to be present inside a folder. So, create a new folder with the name Features in the src/test/resources folder.
Right-click on the src/test/resources folder, and select New →Package. Provide the name of Features to the package and click the Finish button.
Step 5 – Create a Feature file in the features package.
Right-click on the Feature folder, select New ->File and mention the name LoginPage.feature. Click the Finish button. (Remember to add .feature at the end of the file, otherwise, this feature file will be just an ordinary plain text file).
The below image is an example of the new feature file created. This sample feature file gives an idea how what an actual Feature file should look like.
Below is an example of a valid feature file.
@LoginPage
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