Step Definition in Cucumber

HOME

The previous tutorial explained the Feature File in Cucumber. This tutorial explains the step definition in the Cucumber.

To start with, please add the below dependencies to the POM.xml, in the case of the Maven project.

<dependencies>
  
   <dependency>
      <groupId>io.cucumber</groupId>
      <artifactId>cucumber-java</artifactId>
      <version>6.11.0</version>
   </dependency>

   <dependency>
      <groupId>io.cucumber</groupId>
      <artifactId>cucumber-junit</artifactId>
      <version>6.11.0</version>
      <scope>test</scope>
    </dependency>
       
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.13.2</version>
       <scope>test</scope>
   </dependency>
    
</dependencies>

For the Gradle project, add the below dependencies to build.gradle

implementation 'io.cucumber:cucumber-java:6.11.0'
testImplementation 'io.cucumber:cucumber-junit:6.11.0'
testImplementation 'junit:junit:4.13.2'

What is Step Definition?

A Step Definition is a Java method with an expression that links it to one or more Gherkin steps. When Cucumber executes a Gherkin step in a scenario, it will look for a matching step definition to execute.

Cucumber finds the Step Definition file with the help of the Glue code in Cucumber Options.

By storing state in instance variables, a step definition can transfer state to a subsequent step definition.

Step definitions are not associated with a specific feature file or scenario. The name of a step definition’s file, class, or package has no bearing on which Gherkin steps it will match. The formulation of the step definition is the only thing that matters, which means the step definition should only match Gherkin’s steps.

Imagine, we want to test a web application. One of the first steps is Login to the website and then check the various functionalities on the website. We can create a Gherkin step like “I login to the website” and the corresponding step definition of this Gherkin Step. This Gherkin step can be used in multiple feature files, and we don’t need to create the step definition of this Gherkin step for each feature file.

In the previous tutorial, we have seen that when the Feature file is executed without the Step Definition file, the runner shows the missing steps with the snippet in the console.

When Cucumber encounters a Gherkin step without a matching step definition, it will print a step definition snippet with a matching Cucumber Expression. You can use this as a starting point for new step definitions.

 It is very easy to implement all the steps, all you need to do is to copy the complete text marked in the above box and paste it into the MyHolidayDefinitions class.

@Given, @When, and @Then are imported from packages:-

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

Feature File

Feature: Book flight ticket 

@BookOneWayFlight
Scenario: Book Flight for one way trip

Given I live in Dublin with 2 adults and 2 kids
And I want to book one way flight ticket from Dublin to London on 22 Jan 2020
When I search online
Then TripAdvisor should provide me options of flights on 22 Jan 2020
And Cost of my flight should not be more than 50 Euro per person
And Tickets should be refundable

Let me create the step definition for the above Feature file

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

public class MyHolidayDefinitions {

	@Given("I live in Dublin with {int} adults and {int} kids")
	public void liveInDublin(Integer int1, Integer int2) {

		System.out.println("I live in Dublin with 2 adults and 2 kids");
	}

	@Given("I want to book one way flight ticket from Dublin to London on {int} Jan {int}")
	public void bookFlightTicket(Integer int1, Integer int2) {

		System.out.println("I want to book one way flight ticket from Dublin to London on 22 Jan 2020");
	}

	@When("I search online")
	public void searchOnline() {

		System.out.println("I search online");
	}

	@Then("TripAdvisor should provide me options of flights on {int} Jan {int}")
	public void tripAdvisor(Integer int1, Integer int2) {

		System.out.println("TripAdvisor should provide me options of flights on 22 Jan 2020");
	}

	@Then("Cost of my flight should not be more than {int} Euro per person")
	public void costOfFlightLimit(Integer int1) {

		System.out.println("Cost of my flight should not be more than 50 Euro per person");
	}

	@Then("Tickets should be refundable")
	public void refundableTickets() {

		System.out.println("Tickets should be refundable");
	}

}

To run the scenarios present in the Feature File, we need TestRunner class. To know more about the TestRunner class, please refer to this tutorial –

import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources/Features/MyHoliday.feature", tags = "@BookOneWayFlight", glue = "com.cucumber.MyCucumberProject.definitions")

public class CucumberRunnerTest {

}

The output of the above program is

Congratulations. We have created the setup definition for the feature file successfully and are able to run it.

Refer to the next tutorials to know the integration of Cucumber with Selenium – Integration of Cucumber with Selenium and JUnit and JUnit4 and Integration of Cucumber with Selenium and TestNG

Happy Learning!!

How to install Cucumber Eclipse Plugin

HOME

The Cucumber plugin is an Eclipse plugin that allows the 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.

When we create a feature file in Eclipse it looks something like the below without Cucumber Eclipse Plugin installed.

It is easy to install Cucumber Eclipse Plugin, as it comes as a plugin for Eclipse IDE. A prerequisite for installing this plugin is your Internet connection should be up & running during the installation of this plugin and Eclipse IDE should be installed on your computer.

Steps to follow:

  1. Launch the Eclipse IDE and, from the Help menu, click “Install New Software”.

2. You will see a dialog window, click the “Add” button.

3. Type the name as you wish, let’s take “Cucumber” and type “https://cucumber.github.io/cucumber-eclipse-update-site-snapshot” as the location. Click the OK button.

4. You come back to the previous window, but this time you must see the Cucumber Eclipse Plugin option in the available software list. Just check the box and press the “Next” button.

5. Click on the Next Button.

6. Click “I accept the terms of the license agreement” and then click the Finish button.

7. You may or may not encounter a Security warning, if in case you do just click the OK button.

8. You are all done now, just click “Restart Now” button.

After restarting the eclipse, you can see the feature file is highlighted based on the keywords.

This means now the eclipse is able to understand the language we have written in the feature file as gherkin language.

All the steps in the below scenario are highlighted in yellow colour, which indicates we don’t have any corresponding stepdefinition for each step.

Now, in the below example, I have created the step definition for the Given statement. So, now, it is not highlighted in yellow colour.

If you press Ctrl button and place the cursor on Given Statement, it will take you to the corresponding step definition of that step. This is a very helpful feature. When we have multiple feature files with multiple steps, it helps us find the exact location of the step definition.

I hope this tutorial makes your learning a little easy. Thanks. Happy Learning!!

JUnit Tutorials

HOME

JUnit is an open source Unit Testing Framework for JAVA.
JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.

JUnit4

Chapter 1 How to configure Junit in Intellij
Chapter 2 How to run JUnit5 tests through Command Line
Chapter 3 JUnit4 Assertions
Chapter 4 Integration of Cucumber with Selenium and JUnit
Chapter 5 Integration of Serenity with Cucumber6 and JUnit5
Chapter 6 Integration of Serenity with JUnit4
Chapter 7 Rest API Test in Cucumber BDD

JUnit5

Chapter 1 JUnit5 Assertions Example
Chapter 2 Grouped Assertions in JUnit 5 – assertAll()
Chapter 3 How to Retry Test in JUnit5 – @RepeatedTest
Chapter 4 How to disable tests in JUnit5 – @Disabled
Chapter 5 How to run JUnit5 tests in order
Chapter 6 How to tag and filter JUnit5 tests – @Tag
Chapter 7 How to parameterized Tests in JUnit5
Chapter 8 How to run parameterized Selenium test using JUnit5
Chapter 9 Integration of Serenity with JUnit5
Chapter 10 Integration of Serenity with Cucumber6 and JUnit5

Dependency Injection in Cucumber using Pico-Container

HOME

In this tutorial, we will use the constructor injection technique to share web driver instances in multiple step definitions using PicoContainer.

Why do we need Dependency Injection in Cucumber?

A new Framework is built that contains several Page Objects, Step Definitions, Feature files, and Helper Classes. Eventually, new Feature Files will be added that contain the steps that are already present in the existing Step Definition files. In this case, we will prefer to use the existing Step Definitions instead of creating new ones. But, Cucumber does not support Inheritance means it does not allow to extend classes that contain Step Definitions or Hooks (@After, @Before, etc.). Now, Dependency Injection comes into the picture.

In Cucumber, if we want to share state between multiple step definition files, we will need to use dependency injection (DI). There are several options: PicoContainer, Spring, OpenEJB, etc. If you’re not already using DI, then it is recommended to use PicoContainer. Otherwise, use the one that’s already in use, because you should only have one.

To use PicoContainer, add the following dependency to the POM.xml

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-picocontainer</artifactId>
    <version>7.0.0</version>
    <scope>test</scope>
</dependency>

Let me explain this with the help of an example.

Imagine there are 2 feature files. These feature files are using the same browser initialization and website. Now, instead of for creating the browser initialization twice for 2 feature files, why not create a Common Class and mention these details in that class and using DI, call this class in the main Step Definition classes.

Feature File 1 – HomePage.feature

Feature: Home page validation
  
Background:
   Given User Navigates to HRM login page
   And User login with valid credentials
 
   @ValidQuickLaunch
   Scenario Outline: Login with valid credentials to check QuickLanuch options  
     
   When User is in Dashboard page
     Then there are valid QuickLaunch options '<options>'
         
    Examples: 
        | options                  |
        | Assign Leave             |
        | Leave List               |
        | Timesheets               |
 
     
    @ValidLegendOptions    
    Scenario Outline: Login with valid credentials to check Manu Options 
     
   When User is in Dashboard page
     Then there are valid Legend options '<legendOptions>'
         
    Examples: 
        | legendOptions               |
        | Not assigned to Subunits    |
        | Administration              |
        | Client Services             |

Feature File 2 – LoginPage.feature

Feature: Login to HRM Application 
  
   @ValidCredentials
   Scenario: Login with valid credentials
      
    Given User is on Home page
    When User enters username as "Admin"
    And User enters password as "admin123"
    Then User should be able to login sucessfully

Next, create a new class that holds the common data. For example:

public class ApplicationHooks {

	private WebDriver driver;

	@Before
	public void setUp() {
		setDriver();
	}

	public void setDriver() {

		WebDriverManager.chromedriver().setup();
		driver = new ChromeDriver();
		driver.manage().window().maximize();
		driver.get("https://opensource-demo.orangehrmlive.com/");
	}

	public WebDriver getDriver() {
		return driver;
	}

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

Then, in each of your step definition files that you want to use this common data, you can add a constructor that takes StepData as an argument. This is where the injection occurs. For example:

LoginDefinition

public class LoginDefinition {

	private ApplicationHooks hooks;

	public LoginDefinition(ApplicationHooks hooks) {

		this.hooks = hooks;
	}

	@Given("User is on Home page")
	public void userOnHomePage() {

		System.out.println("Home Page is opened");
	}

	@When("User enters username as {string}")
	public void entersUsername(String userName) throws InterruptedException {

		System.out.println("Username Entered");
		hooks.getDriver().findElement(By.name("txtUsername")).sendKeys(userName);

	}

	@When("User enters password as {string}")
	public void entersPassword(String passWord) throws InterruptedException {

		System.out.println("Password Entered");
		hooks.getDriver().findElement(By.name("txtPassword")).sendKeys(passWord);

		hooks.getDriver().findElement(By.id("btnLogin")).submit();
	}

	@Then("User should be able to login sucessfully")
	public void sucessfullLogin() throws InterruptedException {

		String newPageText = hooks.getDriver().findElement(By.id("welcome")).getText();
		System.out.println("newPageText :" + newPageText);
		Assert.assertTrue(newPageText.contains("Welcome"));

	}
}

HomeDefinition

public class HomePageDefinition {

	ApplicationHooks hooks;

	public HomePageDefinition(ApplicationHooks hooks) {
		this.hooks = hooks;
	}

	@Given("User Navigates to HRM login page")
	public void userOnHomePage() {

		System.out.println("HRM login Page is opened");
	}

	@Given("User login with valid credentials")
	public void entersCredentials() throws InterruptedException {

		hooks.getDriver().findElement(By.name("txtUsername")).sendKeys("Admin");
	hooks.getDriver().findElement(By.name("txtPassword")).sendKeys("admin123");
		hooks.getDriver().findElement(By.id("btnLogin")).submit();

	}

	@When("User is in Dashboard page")
	public void verifyDashboardPage() {

		String dashboardTitle = hooks.getDriver().findElement(By.id("welcome")).getText();
		Assert.assertTrue(dashboardTitle.contains("Welcome"));

	}

	@Then("there are valid QuickLaunch options {string}")
	public void verifyQuickLinks(String options) throws InterruptedException {

		switch (options) {
		case "Assign Leave":
			String linkOne = hooks.getDriver()
					.findElement(By.xpath(
							"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[1]/div/a/span"))
					.getText();
			Assert.assertEquals(linkOne, options);

			break;
		case "Leave List ":
			String linkTwo = hooks.getDriver()
					.findElement(By.xpath(
							"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[2]/div/a/span"))
					.getText();
			Assert.assertEquals(linkTwo, options);
			Thread.sleep(1000);
			break;

		case "Timesheets":
			String linkThree = hooks.getDriver()
					.findElement(By.xpath(
							"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[3]/div/a/span"))
					.getText();
			Assert.assertEquals(linkThree, options);
			break;

		default:
			break;
		}

	}

	@Then("there are valid Legend options {string}")
	public void verifyMenuOptions(String options) throws InterruptedException {

		switch (options) {
		case "Not assigned to Subunits":
			String linkOne = hooks.getDriver()
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[1]/td[2]"))
					.getText();
			Assert.assertEquals(linkOne, options);
			break;

		case "Administration":
			String linkTwo = hooks.getDriver()
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[2]/td[2]"))
					.getText();
			Assert.assertEquals(linkTwo, options);
			break;

		case "Client Services":
			String linkThree = hooks.getDriver()
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[3]/td[2]"))
					.getText();
			Assert.assertEquals(linkThree, options);
			break;
		default:
			break;

		}
	}
}

Create a Test Runner Class to execute the tests.

import org.junit.runner.RunWith;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)

@CucumberOptions(features= {"src/test/resources"}, glue= {"com.cucumber"})
public class RunCucumberTest {

}

Execute the tests either through JUnit Runner or Command-Line using maven.

The test Report can be accessed from the link provided in the execution status:

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Serenity Emailable Report in Gradle

HOME

In this tutorial, I will generate an emailable Serenity Report for Gradle project. In the previous tutorial, I have explained the Generation of Serenity Emailable Report in Maven Project.

Pre-Requisite

  1. Java 11 installed
  2. Gradle installed
  3. Eclipse or IntelliJ installed

This framework consists of:

  1. Serenity – 2.6.0
  2. Serenity Cucumber – 2.6.0
  3. Java 11
  4. JUnit – 4.13.2
  5. Gradle – 7.2

Steps to create Serenity Emailable Report

To setup a Gradle project for the testing of web application using Cucumber and JUnit4, please refer this tutorial (Step 1 to 3)

Update buildscript section of build.gradle file.

buildscript {
    repositories {
        mavenLocal()
        jcenter()
    }
    dependencies {
        classpath("net.serenity-bdd:serenity-gradle-plugin:2.4.24")
        classpath("net.serenity-bdd:serenity-single-page-report:2.4.24")
    }
}

Add serenity section in build.gradle.

serenity {
    reports = ["single-page-html"]
}

The complete build.gradle for the project will look like as shown below

defaultTasks 'clean', 'test', 'aggregate'

repositories {
    mavenLocal()
    jcenter()
}

buildscript {
    repositories {
        mavenLocal()
        jcenter()
    }
    dependencies {
        classpath("net.serenity-bdd:serenity-gradle-plugin:2.4.24")
        classpath("net.serenity-bdd:serenity-single-page-report:2.4.24")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'net.serenity-bdd.aggregator'

sourceCompatibility = 11
targetCompatibility = 11

serenity {
    reports = ["single-page-html"]
}

dependencies {
   
    testImplementation 'net.serenity-bdd:serenity-core:2.6.0'
    testImplementation 'net.serenity-bdd:serenity-cucumber6:2.6.0'
    testImplementation 'net.serenity-bdd:serenity-screenplay:2.6.0'
    testImplementation 'net.serenity-bdd:serenity-screenplay-webdriver:2.6.0'
    testImplementation 'junit:junit:4.13.1'
}

test {
    testLogging.showStandardStreams = true
    systemProperties System.getProperties()
}

gradle.startParameter.continueOnFailure = true

test.finalizedBy(aggregate)

Execute the test suite by using the below command.

gradle test

This will generate only index.html not serenity-summary.html (emailable) report.

To generate single page html report, we need to invoke the report task.

gradle reports

Below is the image of serenity-summary.html report.

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Serenity BDD with Gradle and Cucumber for Web Application

HOME

In the previous tutorial, I have explained about Integration Testing of SpringBoot Application with Serenity BDD and Cucumber in Maven project. This tutorial describes the creation of the Gradle Java Project to test a web application using Cucumber6 and JUnit4.

In this tutorial, I will explain creating a framework for the testing of Web Application in Cucumber BDD.

Pre-Requisite

  1. Java 11 installed
  2. Gradle installed
  3. Eclipse or IntelliJ installed

This framework consists of:

  1. Serenity – 2.6.0
  2. Serenity Cucumber – 2.6.0
  3. Java 11
  4. JUnit – 4.13.2
  5. Gradle – 7.2

Steps to setup Gradle Java Project for Web Application using Serenity , Cucumber6 and JUnit4

  1. Download and Install Java on the system
  2. Download and setup Eclipse IDE on the system
  3. Setup Gradle on System and create a new Gradle Project
  4. Update repositories, plugin and dependencies to the Gradle project
  5. Create a feature file under src/test/resources
  6. Create the Step Definition class or Glue Code for the Test Scenario
  7. Create a Serenity Cucumber Runner class
  8. Create serenity.conf file under src/test/resources
  9. Create serenity.properties file in the root of the project
  10. Run the tests through commandline which generates Serenity Report

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 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. Click here to know How to create a Gradle Java project. Below is the structure of Gradle project.

Step 4 – Update repositories, plugin and dependencies to the Gradle project

defaultTasks 'clean', 'test', 'aggregate'

repositories {
    mavenLocal()
    jcenter()
}

buildscript {
    repositories {
        mavenLocal()
        jcenter()
    }
    dependencies {
        classpath("net.serenity-bdd:serenity-gradle-plugin:2.4.24")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'net.serenity-bdd.aggregator'

sourceCompatibility = 11
targetCompatibility = 11

dependencies {
   
    testImplementation 'net.serenity-bdd:serenity-core:2.6.0'
    testImplementation 'net.serenity-bdd:serenity-cucumber6:2.6.0'
    testImplementation 'net.serenity-bdd:serenity-screenplay:2.6.0'
    testImplementation 'net.serenity-bdd:serenity-screenplay-webdriver:2.6.0'
    testImplementation 'junit:junit:4.13.1'
}

test {
    testLogging.showStandardStreams = true
    systemProperties System.getProperties()
}

gradle.startParameter.continueOnFailure = true

test.finalizedBy(aggregate)

Step 5 – Create a feature file under src/test/resources

A Feature File is an entry point to the Cucumber tests. This is a file where you will describe your tests in Descriptive language (Like English). A feature file can contain a scenario or can contain many scenarios in a single feature file. Below is an example of Feature file.

Feature: Login to HRM  

   @ValidCredentials
   Scenario: Login with valid credentials
   
    Given User is on Home page
    When User enters username as "Admin"
    And User enters password as "admin123"
    Then User should be able to login successfully

Step 6 – Create the Step Definition class or Glue Code for the Test Scenario

Steps definition file stores the mapping between each step of the test scenario defined in the feature file with a code of function to be executed. So, now when Cucumber executes a step of the scenario mentioned in the feature file, it scans the step definition file and figures out which function is to be called.

Create a StepDefinition class for LoginPage.feature

public class LoginPageDefinitions {
 
    @Steps
    StepLoginPage loginPage;
 
    @Steps
    StepDashboardPage dashPage;
 
    @Steps
    StepForgetPasswordPage forgetpasswordPage;
 
    @Given("User is on Home page")
    public void openApplication() {
        loginPage.open();
        System.out.println("Page is opened");
    }
 
    @When("User enters username as {string}")
    public void enterUsername(String userName) {
        System.out.println("Enter Username");
        loginPage.inputUserName(userName);
    }
 
    @When("User enters password as {string}")
    public void enterPassword(String passWord) {
        loginPage.inputPassword(passWord);
 
        loginPage.clickLogin();
    }
 
    @Then("User should be able to login successfully")
    public void clickOnLoginButton() {
        dashPage.loginVerify();
    }
   
}

Serenity Step Libraries integrate smoothly into Cucumber Step Definition files; all you need to do is to annotate a step library variable with the @Steps annotation.  Methods that represent a business task or action (inputUserName()), and that will appear in the reports as a separate step, are annotated with the @Step annotation. Here, I have created two StepClasses – StepLoginPage and StepDashboardPage

public class StepLoginPage extends PageObject {
 
    @Step("Enter Username")
    public void inputUserName(String userName) {
        $(By.name("txtUsername")).sendKeys((userName));
    }
 
    @Step("Enter Password")
    public void inputPassword(String passWord) {
        $(By.name("txtPassword")).sendKeys((passWord));
    }
 
    @Step("Click Submit Button")
    public void clickLogin() {
        $(By.name("Submit")).click();
    } 
 
}

StepDashboardPage

public class StepDashboardPage extends PageObject {
 
    @Step("Successful login")
    public void loginVerify() {
        String dashboardTitle = $(By.id("welcome")).getText();
        assertThat(dashboardTitle, containsString("Welcome"));
    }
}

Step 7 – Create a Serenity Cucumber Runner class

import org.junit.runner.RunWith;

import io.cucumber.junit.CucumberOptions;
import net.serenitybdd.cucumber.CucumberWithSerenity;

@RunWith(CucumberWithSerenity.class)
@CucumberOptions(plugin = {}, features = "lib/src/test/resources/features", glue = "serenitygradleautomation.definitions")

public class CucumberTestSuite {

}

Step 8 – Create serenity.conf file under src/test/resources

Serenity.conf file is used to specify various features like type of webdriver used, various test environments, run test in headless mode and many more options.

webdriver {
    driver = firefox
}
 
 
environments {
  default {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/"
  }
  dev {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/dev"
  }
  staging {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/staging"
  }
  prod {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/prod"
  }
}

Step 9 – Create serenity.properties file in the root of the project

serenity.project.name = Serenity and Cucumber Gradle Demo

Step 10 – Run the tests through commandline which generates Serenity Report

Open commandline and go to the location where gradle.build of the project is present and type the below command.

gradle test

The Serenity report is generated under /lib/target/site/serenity.

Serenity Report

Below is the image of Overall Test Result with steps and screenshots.

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

Serenity Report for Web Application with Cucumber6 and Junit

HOME

In the previous tutorial, I have explained about the Testing of Web Application using Serenity BDD with Cucumber5 and JUnit4. In this tutorial, I’ll explain how to generate a Serenity Report for web application using Serenity BDD with Cucumber6 and JUnit4.

Serenity BDD produces great test reports which act as Living Documentation for the product.

Pre-Requisite

  1. Java 11 installed
  2. Maven installed
  3. Eclipse or IntelliJ installed

This framework consists of:

  1. Java 11
  2. Maven – 3.8.1
  3. Serenity – 2.6.0
  4. Serenity Maven – 2.6.0
  5. Serenity Cucumber6 – 2.6.0
  6. JUnit – 4.13.2
  7. Maven Surefire Plugin – 3.0.0-M5
  8. Maven Failsafe Plugin – 3.0.0-M5
  9. Maven Compiler Plugin – 3.8.1

Implementation Steps

  1. Update Properties section in Maven pom.xml
  2. Add repositories and pluginRepository to Maven pom.xml
  3. Add Serenity, Serenity Cucumber and JUnit dependencies to POM.xml
  4. Update Build Section of pom.xml
  5. Create source folder – src/test/resources and features folder within src/test/resources to create test scenarios in Feature file
  6. Create the Step Definition class or Glue Code
  7. Create a Serenity-Cucumber Runner class
  8. Create serenity.conf file under src/test/resources
  9. Create serenity.properties file in the root of the project
  10. Run the tests through commandline which generates Serenity Report

Step 1 – Update Properties section in Maven pom.xml

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <serenity.version>2.6.0</serenity.version>
    <serenity.maven.version>2.6.0</serenity.maven.version>
    <serenity.cucumber.version>2.6.0</serenity.cucumber.version>
    <junit.version>4.13.2</junit.version>
    <maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
    <maven.failsafe.plugin.version>3.0.0-M5</maven.failsafe.plugin.version>
    <maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>   
    <encoding>UTF-8</encoding>
        <tags></tags>
        <parallel.tests>4</parallel.tests>
        <webdriver.base.url></webdriver.base.url>
  </properties>

Step 2 – Add repositories and pluginRepository to Maven pom.xml

<repositories>
        <repository>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <id>central</id>
            <name>bintray</name>
            <url>https://jcenter.bintray.com</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <id>central</id>
            <name>bintray-plugins</name>
            <url>https://jcenter.bintray.com</url>
        </pluginRepository>
    </pluginRepositories>

Step 3 – Add Serenity, Serenity Cucumber and JUnit dependencies to POM.xml

<dependencies>

   <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-core</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-junit</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-rest-assured</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-screenplay</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-cucumber6</artifactId>
            <version>${serenity.cucumber.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-screenplay-webdriver</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
           
    </dependencies>

Step 4 – Update Build Section of pom.xml

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven.surefire.plugin.version}</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>${maven.failsafe.plugin.version}</version>
                <configuration>
                    <includes>
                        <include>**/*.java</include>
                    </includes>
                    <parallel>methods</parallel>
                    <useUnlimitedThreads>true</useUnlimitedThreads>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.version}</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
           <plugin>
                <groupId>net.serenity-bdd.maven.plugins</groupId>
                <artifactId>serenity-maven-plugin</artifactId>
                <version>${serenity.maven.version}</version>
                <configuration>
                    <tags>${tags}</tags>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>net.serenity-bdd</groupId>
                        <artifactId>serenity-core</artifactId>
                        <version>${serenity.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <id>serenity-reports</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>aggregate</goal>
                        </goals>
                    </execution>
                </executions>                     
            </plugin>
        </plugins>
    </build>

Step 5 – Create source folder – src/test/resources and features folder within src/test/resources to create test scenarios in Feature file

Feature file should be saved as an extension of .feature. Add the test scenarios in this feature file. I have added sample test scenarios. The test scenarios are written in Gherkins language.

Feature: Login to HRM  

   @ValidCredentials
   Scenario: Login with valid credentials
   
    Given User is on Home page
    When User enters username as "Admin"
    And User enters password as "admin123"
    Then User should be able to login successfully
    
    @InValidCredentials    
    Scenario Outline: Login with invalid credentials
   
    Given User is on Home page
    When User enters username as '<username>'
    And User enters password as '<password>'
    Then User should be able to see error message '<errorMessage>'
      
   Examples:
    |username  |password  |errorMessage                    |
    |admin     |admin     |Invalid credentials             |
    |          |admin123  |Username cannot be empty        | 
    |Admin     |          |Password cannot be empty        |
    |          |          |Username cannot be empty        |
 
   @ForgetPassword  
   Scenario: Verify Forget Password Functionality
   
    Given User is on Home page
    When User clicks on Forgot your password link
    Then User should be able to see new page which contains Reset Password button
   

Step 6 – Create the Step Definition class or Glue Code

Create a StepDefinition class for LoginPage.feature.

public class LoginPageDefinitions {

	@Steps
	StepLoginPage loginPage;

	@Steps
	StepDashboardPage dashPage;

	@Steps
	StepForgetPasswordPage forgetpasswordPage;

	@Given("User is on Home page")
	public void openApplication() {
		loginPage.open();
		System.out.println("Page is opened");
	}

	@When("User enters username as {string}")
	public void enterUsername(String userName) {
		System.out.println("Enter Username");
		loginPage.inputUserName(userName);
	}

	@When("User enters password as {string}")
	public void enterPassword(String passWord) {
		loginPage.inputPassword(passWord);

		loginPage.clickLogin();
	}

	@Then("User should be able to login successfully")
	public void clickOnLoginButton() {
		dashPage.loginVerify();
	}

	@Then("User should be able to see error message {string}")
	public void unsucessfulLogin(String expectedErrorMessage) throws InterruptedException {
		String actualErrorMessage = loginPage.errorMessage()
		Assert.assertEquals(expectedErrorMessage, actualErrorMessage);
	}

	@When("User clicks on Forgot your password link")
	public void clickForgetPasswordLink() {
		loginPage.clickForgetPasswordLink();
	}

	@Then("User should be able to see new page which contains Reset Password button")
	public void verifyForgetPasswordPage() {

		Assert.assertTrue(forgetpasswordPage.ForgetPasswordPage());
    }
}
	

Serenity Step Libraries integrate smoothly into Cucumber Step Definition files; all you need to do is to annotate a step library variable with the @Steps annotation. Methods that represent a business task or action (inputUserName()), and that will appear in the reports as a separate step, are annotated with the @Step annotation. Other methods, such as loginVerify(), query the state of the application and are used in assert statements.

Here, I have created 3 StepClasses – StepLoginPage, StepDashboardPage and StepForgetPasswordPage

public class StepLoginPage extends PageObject {

	@Step("Enter Username")
	public void inputUserName(String userName) {
		$(By.name("txtUsername")).sendKeys((userName));
	}

	@Step("Enter Password")
	public void inputPassword(String passWord) {
		$(By.name("txtPassword")).sendKeys((passWord));
	}

	@Step("Click Submit Button")
	public void clickLogin() {
		$(By.name("Submit")).click();
	}

	@Step("Error Message on unsuccessful login")
	public String errorMessage() {
		String actualErrorMessage = $(By.id("spanMessage")).getText();
		return actualErrorMessage;
	}

	@Step("Click Forget Password Link")
	public void clickForgetPasswordLink() {
		$(By.linkText("Forgot your password?")).click();
	
	}
}

StepDashboardPage

public class StepDashboardPage extends PageObject {

	@Step("Successful login")
	public void loginVerify() {
		String dashboardTitle = $(By.id("welcome")).getText();
		assertThat(dashboardTitle, containsString("Welcome"));
	}
}

StepForgetPasswordPage

public class StepForgetPasswordPage extends PageObject {

	@Step("Verify Forget Password Page ")
	public boolean ForgetPasswordPage() {
		Boolean resetPasswordButton = $(By.id("btnSearchValues")).isDisplayed();

		return resetPasswordButton;
	}
}

Step 7 – Create a Serenity-Cucumber Runner class

We cannot run a Feature file by its own in cucumber based framework. We need to create a Java class, which will run the Feature File. It is the starting point for JUnit to start executing the tests. TestRunner class creates under src/ test/java. When you run the tests with serenity, you use the CucumberWithSerenity test runner. If the feature files are not in the same package as the test runner class, you also need to use the @CucumberOptions class to provide the root directory where the feature files found.

import org.junit.runner.RunWith;

import io.cucumber.junit.CucumberOptions;
import net.serenitybdd.cucumber.CucumberWithSerenity;

@RunWith(CucumberWithSerenity.class)
@CucumberOptions(plugin = {}, features = "src/test/resources/features/Login/LoginPage.feature", glue = "com.example.SerenityReportDemo.definitions")

public class CucumberRunnerTest {

}

Step 8 – Create serenity.conf file under src/test/resources

Serenity.conf file is used to specify various features like type of webdriver used, various test environments, run test in headless mode and many more options.

webdriver.driver. – This tells Serenity which browser to use for the test execution. You can configure this in several locations – serenity.properties or serenity.conf. Here, I have provided this information in serenity.conf

We can also configure the webdriver.base.url property for different environments in the serenity.conf configuration file, in the src/test/resources directory. Below is the example of the same.

webdriver {
    driver = firefox
}


environments {
  default {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/"
  }
  dev {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/dev"
  }
  staging {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/staging"
  }
  prod {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/prod"
  }
}

Once the environment section is present in your serenity.conf file, you can use the environment system property to use the properties for a given environment. For example, the following would cause the staging urls to be used:

mvn verify -Denvironment=staging

The default environment will be used if no other value is provided. In our example, I will not provide any environment, so it will pick the default environment.

Step 9 – Create serenity.properties file in the root of the project

serenity.project.name = Serenity and Cucumber Report Demo

Step 10 – Run the tests through commandline which generates Serenity Report

Open commandline and go to the location where pom.xml of the project is present and type the below command.

mvn verify -Dwebdriver.gecko.driver="C:\\Users\\Vibha\\Software\\geckodriver-v0.26.0-win64\\geckodriver.exe"

I have provided the location of firefoxdriver through commandline. I believe this is the best way to run the test. We can hard-code the path in the test code or in serenity.conf file. If you don’t want to pass the location of webdriver through commandline, then mention the details of webdriver in serenity.confi and just use the below command for execution.

mvn clean verify

Below is the image of execution status.

This also provides the location of serenity report as highlighted in the above image.

Serenity Report

Requirement View

In Serenity, requirements are organised in a hierarchy. We can get an idea of the full directory structure (in src/test/features directory) for the project.

The Test Results tab (shown below) tells you about the acceptance tests that were executed for this set of requirements. 

The Functional Coverage section shows the test results broken down by functional area.

Test Results

At the bottom of the Test Results tab, you will find the actual test results – the list of all the tests, automated and manual, that were executed for this requirement.

Capability

Feature

This provide the detail of all the Test Scenarios present in a Feature File.

Below is an example of Scenario Outline in the Report. It shows all the examples as mentioned the feature file.

This screen show the test steps and screenshot of each step.

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Allure Report with Cucumber5 and JUnit4

HOME

In the previous tutorial, I have explained the Integration of the Allure Report with Selenium and JUnit4. In this tutorial, I will explain how to Integrate Allure Report with Cucumber5 and JUnit4.

Below example covers the implementation of Allure Reports in Selenium using JUnit4, Java and Maven.

Pre-Requisite

  1. Java 11 installed
  2. Maven installed
  3. Eclipse or IntelliJ installed

This framework consists of:

  1. Selenium – 3.141.59
  2. Java 11
  3. Cucumber 5 – 5.7.0
  4. Maven – 3.8.1
  5. Allure Report – 2.14.0
  6. Allure Cucumber5 – 2.14.0
  7. Allure JUnit4 – 2.14.0
  8. Aspectj – 1.9.6

Implementation Steps

  1. Update Properties section in Maven pom.xml
  2. Add Cucumber5, Selenium, JUnit4, Allure Cucumber5 and Allure-JUnit4 dependencies in POM.xml
  3. Update Build Section of pom.xml in Allure Report Project.
  4. Create source folder – src/test/resources and features folder within src/test/resources to create test scenarios in Feature file
  5. Create the Step Definition class or Glue Code
  6. Create a Cucumber Runner class
  7. Run the Test and Generate Allure Report

Step 1 – Update Properties section in Maven pom.xml

 <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <selenium.version>3.141.59</selenium.version> 
    <cucumber.version>5.7.0</cucumber.version>
    <allure.cucumber5.version>2.14.0</allure.cucumber5.version>
    <allure.junit4.version>2.14.0</allure.junit4.version>
    <maven.compiler.plugin.version>3.5.1</maven.compiler.plugin.version>
    <maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <aspectj.version>1.9.6</aspectj.version>
    <allure.version>2.14</allure.version>
  </properties>

Step 2 – Add Cucumber5, Selenium, JUnit4, Allure-Cucumber5 and Allure-JUnit4 dependencies in POM.xml

 <dependencies>
     
     <!--Cucumber 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-->
    <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>${selenium.version}</version>
    </dependency>   

   <!--Hamcrest Dependency-->
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest</artifactId>
      <version>2.2</version>
      <scope>test</scope>
    </dependency>
    
   <!--Allure Cucumber Dependency-->     
      <dependency>
        <groupId>io.qameta.allure</groupId>
        <artifactId>allure-cucumber5-jvm</artifactId>
        <version>${allure.cucumber5.version}</version>
    </dependency>
    
     <!--Allure Reporting Dependency-->     
    <dependency>
        <groupId>io.qameta.allure</groupId>
        <artifactId>allure-junit4</artifactId>
        <version>${allure.junit4.version}</version>
        <scope>test</scope>
    </dependency>

  </dependencies>

Step 3 – Update Build Section of pom.xml in Allure Report Project

<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}</source> 
                    <target>${maven.compiler.target}</target>
                </configuration>
         </plugin>
     
       <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
             <version>${maven.surefire.plugin.version}</version>
            <configuration>
                 <systemPropertyVariables>
                        <allure.results.directory>${project.build.directory}/allure-results</allure.results.directory>
                    </systemPropertyVariables>
                <argLine>
                    -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                    -Dcucumber.options="--plugin io.qameta.allure.cucumber5jvm.AllureCucumber5Jvm"
                </argLine>
                        
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjweaver</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
            </dependencies>
         </plugin>
       
     </plugins> 
  </build>

Step 4 – Create source folder – src/test/resources and features folder within src/test/resources to create test scenarios in Feature file

Feature file should be saved as an extension of .feature. Add the test scenarios in this feature file. I have added sample test scenarios. In this feature file, I have created a scenario for successful login and one Scenario Outline for failed login. The test scenarios are written in Gherkins language.

@LoginPage @Junit4

@severity=blocker

Feature: Feature - Login to HRM Application 
 
   @ValidCredentials
   Scenario: Scenario - Login with valid credentials 
     
    Given User is on Home page
    When User enters username as "Admin"
    And User enters password as "admin123"
    Then User should be able to login sucessfully
    
   @InvalidCredentials
   Scenario Outline: Scenario -Login with invalid credentials
   
    Given User is on Home page
    When User enters username as "<username>"
    And User enters password as "<password>"
    Then Error message "<message>" should be displayed
    
    Examples:
    |username  |password  |message                         |
    |admin     |admin     |Invalid credentials             |
    |          |admin123  |Username cannot be empty        | 
    |Admin     |          |Password cannot be empty        |
    |          |          |Username can be empty           |

Step 5 – Create the Step Definition class or Glue Code

public class LoginDefinition {

	@Given("User is on Home page")
	public void userOnHomePage() {

		CommonDefinitions.driver.get("https://opensource-demo.orangehrmlive.com/");
	}

	@When("User enters username as {string}")
	public void entersUsername(String userName) throws InterruptedException {

		System.out.println("Username Entered");
		CommonDefinitions.driver.findElement(By.name("txtUsername")).sendKeys(userName);

	}

	@When("User enters password as {string}")
	public void entersPassword(String passWord) throws InterruptedException {

		System.out.println("Password Entered");
		CommonDefinitions.driver.findElement(By.name("txtPassword")).sendKeys(passWord);

		CommonDefinitions.driver.findElement(By.id("btnLogin")).submit();
	}

	@Then("User should be able to login sucessfully")
	public void sucessfulLogin() throws InterruptedException {

		String newPageText = CommonDefinitions.driver.findElement(By.id("welcome")).getText();
		System.out.println("newPageText :" + newPageText);
		assertThat(newPageText, containsString("Welcome"));

	}

	@Then("Error message {string} should be displayed")
	public void unsucessfulLogin(String message) throws InterruptedException {

		String errorMessage = CommonDefinitions.driver.findElement(By.id("spanMessage")).getText();
		System.out.println("Error Message :" + errorMessage);
		Assert.assertEquals(errorMessage, message);

	}

}

CommonDefinitions.java

public class CommonDefinitions {

	protected static WebDriver driver;

	@Before
	public void setup() {
		System.setProperty("webdriver.gecko.driver",
				"C:\\Users\\Vibha\\Software\\geckodriver-v0.26.0-win64\\geckodriver.exe");

		driver = new FirefoxDriver();

		driver.manage().window().maximize();
		driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
	}

	@After
	public void tearDown(Scenario scenario) {
		try {
			String screenshotName = scenario.getName().replaceAll("", "_");
			if (scenario.isFailed()) {
				TakesScreenshot ts = (TakesScreenshot) driver;
				byte[] screenshot = ts.getScreenshotAs(OutputType.BYTES);
				scenario.attach(screenshot, "img/png", screenshotName);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		driver.quit();
	}

}

Step 6 – Create a Cucumber Runner class

We need to create a class called Runner class to run the tests. This class will use the JUnit annotation @RunWith(), which tells JUnit what is the test runner class.

import org.junit.runner.RunWith;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)
@CucumberOptions(features = {
		"src/test/resources/features/Login.feature" }, glue = "com.example.CucumberJunitAllureReportDemo.definitions", plugin = {
				"pretty", "html:test-output", "json:target/cucumber-report/cucumber.json" })

public class CucumberRunnerTests {

}

Step 7 – Run the Test and Generate Allure Report

To run the tests, use the below command

mvn clean test

In the below image, we can see that one test is failed and four passed out of five tests.

This will create allure-results folder with all the test report. These files will be use to generate Allure Report.

Change current directory to target directory and then the below comand to generate the Allure Report

allure serve

This will generate the beautiful Allure Test Report as shown below.

Allure Report Dashboard

It shows detail of all the test steps and the screenshot of the failed test step also as shown below.

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.

Integration Testing of Springboot with Cucumber and JUnit4

HOME

In this tutorial, I am going to build an automation framework to test Springboot application with Cucumber and Rest Assured.

What is Springboot?

Spring Boot is an open-source micro framework maintained by a company called Pivotal. It provides Java developers with a platform to get started with an auto configurable production-grade Spring application. With it, developers can get started quickly without losing time on preparing and configuring their Spring application.

What is Cucumber?

Cucumber is a software tool that supports behavior-driven development (BDD). Cucumber can be defined as a testing framework, driven by plain English. It serves as documentation, automated tests, and a development aid – all in one.

This framework consists of:

  1. Springboot – 2.5.2
  2. Cucumber – 6.10.4
  3. Java 11
  4. JUnit – 4.13.2
  5. Maven – 3.8.1
  6. RestAssured – 4.3.3
  7. Junit Vintage Engine (To run the tests through commandline)

Steps to setup Cucumber Test Automation Framework for API Testing using Rest-Assured

  1. Add SpringbootTest, Rest-Assured, JUnit and Cucumber dependencies to the project
  2. Create a directory src/test/resources and create a feature file under src/test/resources
  3. Create the Step Definition class or Glue Code for the Test Scenario under src/test/java
  4. Create a Cucumber Runner class under src/test/java
  5. Run the tests from JUnit
  6. Run the tests from Command Line
  7. Cucumber Report Generation

Below is the structure of a SpringBoot application project

Below are various Java classes present in a SpringBoot REST Application.

  • SpringBootRestServiceApplication.java – The Spring Boot Application class generated with Spring Initializer. This class acts as the launching point for application.
  • pom.xml – Contains all the dependencies needed to build this project. 
  • Student.java – This is JPA Entity for Student class
  • StudentRepository.java – This is JPA Repository for Student. This is created using Spring Data JpaRepository.
  • StudentController.java – Spring Rest Controller exposing all services on the student resource.
  • CustomizedExceptionHandler.java – This implements global exception handling and customize the responses based on the exception type.
  • ErrorDetails.java – Response Bean to use when exceptions are thrown from API.
  • StudentNotFoundException.java – Exception thrown from resources when student is not found.
  • data.sql –  Data is loaded from data.sql into Student table. Spring Boot would execute this script after the tables are created from the entities.
  • application.properties – Spring Boot automatically loads the application.properties whenever it starts up. You can de reference values from the property file in the java code through the environment.

We need below files to create a SpringBoot Application.

Student.java

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
public class Student {
    @Id
    @GeneratedValue
    private Long id;

    @NotNull
    @Size(min = 4, message = "Name should have atleast 4 characters")
    private String name;

    @NotBlank(message = "passportNumber is mandatory")
    private String passportNumber;

    public Student() {
        super();
    }

    public Student(Long id, String name, String passportNumber) {
        super();
        this.id = id;
        this.name = name;
        this.passportNumber = passportNumber;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassportNumber() {
        return passportNumber;
    }

    public void setPassportNumber(String passportNumber) {
        this.passportNumber = passportNumber;
    }
}

StudentController

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

import java.net.URI;
import java.util.List;
import java.util.Optional;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

@RestController
public class StudentController {

    @Autowired
    private StudentRepository studentRepository;

    @GetMapping("/students/{id}")
    public EntityModel<Student> retrieveStudent(@PathVariable long id) {
        Optional<Student> student = studentRepository.findById(id);

        if (!student.isPresent())
            throw new StudentNotFoundException("id-" + id);

        EntityModel<Student> resource = EntityModel.of(student.get());

        WebMvcLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllStudents());

        resource.add(linkTo.withRel("all-students"));

        return resource;
    }
}

StudentRepository

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends JpaRepository<Student, Long>{

}

SpringBootRestServiceApplication

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootRestServiceApplication {

    public static void main(String[] args) {

        SpringApplication.run(SpringBootRestServiceApplication.class, args);
    }

}

application.properties

spring.jpa.defer-datasource-initialization=true

data.sql

insert into student values(10001,'Annie', 'E1234567');
insert into student values(10002,'John', 'A1234568');
insert into student values(10003,'David','C1232268');

Test Automation Framework Implementation

Step 1 – Add SpringbootTest, Rest-Assured, and Cucumber dependencies to the project

To Test a SpringBoot Application, we are using SpringBoot Test, JUnit, Cucumber, and Rest Assured. Below mentioned dependencies are added in POM.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>4.3.3</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>6.10.4</version>
        </dependency>

        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>6.10.4</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-spring</artifactId>
            <version>6.10.4</version>
            <scope>test</scope>
        </dependency>

Step 2 – Create a directory src/test/resources and create a feature file under src/test/resources

By default, the Maven project has src/test/java directory only. Create a new directory under src/test with the name of resources. Create a folder name as Features within src/test/resources directory.

Create a feature file to test the Springboot application. Below is a sample feature file.

Feature: Verify springboot application using Cucumber

@ReceiveUserDetails
Scenario Outline: Send a valid Request to get user details
Given I send a request to the URL "/students" to get user details
Then the response will return status 200 and id <studentID> and names "<studentNames>" and passport_no "<studentPassportNo>"

Examples:
|studentID    |studentNames  |studentPassportNo|
|10001        |Annie         |E1234567         |
|10002        |John          |A1234568         |
|10003        |David         |C1232268         |

Step 3 – Create the Step Definition class or Glue Code for the Test Scenario under src/test/java

The corresponding step definition file of the above feature file is shown below.

import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.spring.CucumberContextConfiguration;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse;
import io.restassured.specification.RequestSpecification;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;

@CucumberContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringbootCucumberTestDefinitions {

    private final static String BASE_URI = "http://localhost";

    @LocalServerPort
    private int port;

    private ValidatableResponse validatableResponse;

    private void configureRestAssured() {
        RestAssured.baseURI = BASE_URI;
        RestAssured.port = port;
    }

    protected RequestSpecification requestSpecification() {
        configureRestAssured();
        return given();
    }

    @Given("I send a request to the URL {string} to get user details")
    public void iSendARequest(String endpoint) throws Throwable {
        validatableResponse = requestSpecification().contentType(ContentType.JSON)
                .when().get(endpoint).then();
        System.out.println("RESPONSE :"+validatableResponse.extract().asString());
    }

    @Then("the response will return status {int} and id {int} and names {string} and passport_no {string}")
    public void extractResponse(int status, int id, String studentName,String passportNo) {
        validatableResponse.assertThat().statusCode(equalTo(status))
                .body("id",hasItem(id)).body(containsString(studentName))
                .body(containsString(passportNo));

    }

}

The @CucumberContextConfiguration annotation tells Cucumber to use this class as the test context configuration for Spring. It is imported from:-

import io.cucumber.spring.CucumberContextConfiguration;

With the @SpringBootTest annotation, Spring Boot provides a convenient way to start up an application context to be used in a test.  It is imported from package:-

import org.springframework.boot.test.context.SpringBootTest;

By default, @SpringBootTest does not start the webEnvironment to refine further how your tests run. It has several options: MOCK(default), RANDOM_PORT, DEFINED_PORT, NONE.

RANDOM_PORT loads a WebServerApplicationContext and provides a real web environment. The embedded server is started and listens on a random port. LocalServerPort is imported from package:-

import org.springframework.boot.web.server.LocalServerPort;

The assertions are imported from the Hamcrest package:-

import static org.hamcrest.Matchers.*;

Step 4 – Create a Cucumber Runner class under src/test/java

A runner will help us to run the feature file and acts as an interlink between the feature file and StepDefinition Class. To know more about Runner, refer to this link. The TestRunner should be created within the directory src/test/java.

import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)
@CucumberOptions(plugin = "pretty", features = {"src/test/resources/Features"}, glue = { "com.example.demo.definitions"})

public class CucumberRunnerTests {
}

The @CucumberOptions annotation is responsible for pointing to the right feature package, configuring the plugin for a better reporting of tests in the console output, and specifying the package where extra glue classes may be found. We use it to load configuration and classes that are shared between tests.

Step 5 – Run the tests from JUnit

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

In case you are using IntelliJ, select “Run CucumberRunnerTests“.

SpringBootTest creates an application context containing all the objects we need for the Integration Testing It, starts the embedded server, creates a web environment, and then enables methods to do Integration testing.

Step 6 – Run the tests from Command Line

To run the tests from the command line, we need to add junit-vintage-engine dependency. Starting with Spring Boot 2.4, JUnit 5’s vintage engine has been removed from spring-boot-starter-test. If we still want to write tests using JUnit 4, we need to add the following Maven dependency:

        <dependency>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
            <scope>test</scope>
        </dependency>

Use the below command to run the tests:-

mvn test

Step 7 – Cucumber Report Generation

To get Cucumber Test Reports, add cucumber.properties under src/test/resources and add the below instruction in the file. To know more about Cucumber Report Service, refer to this tutorial.

cucumber.publish.enabled=true

Below is the image of the report generated post the completion of the execution. This report can be saved in GitHub for future use.

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

Run Gradle Cucumber Tests from Command Line

HOME

The implementation of a test framework is considered successful and effective, if the test framework supports test execution in multiple ways.
The tests in a Gradle Cucumber Framework can be executed as JUnit Tests, Gradle Tests and Gradle commands from Command Line.

In this tutorial, I will explain to run Gradle tests from Command Line.

To execute tests using JUnit Tests and Gradle Tests, we need to create a JUnit TestRunner.

Steps to follow

  1. Create a Gradle Java Project.
  2. Add Rest-Assured and Cucumber dependencies to the Gradle project
  3. Add Configuration to build.gradle
  4. Add Gradle Cucumber Task to build.gradle
  5. Create a feature file under src/test/resources
  6. Create the Step Definition class or Glue Code for the Test Scenario
  7. Run the tests from Command Line

Step 1 – Create a Gradle project

Step 2 – Add the below mention dependencies in the Gradle project in build.gradle.

plugins {
    // Apply the java-library plugin to add support for Java Library

    id 'java-library'
 
}

repositories {
    // Use jcenter for resolving dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
    mavenCentral()
}

dependencies {
    // This dependency is exported to consumers, that is to say found on their compile classpath.
    api 'org.apache.commons:commons-math3:3.6.1'

    // This dependency is used internally, and not exposed to consumers on their own compile classpath.
    implementation 'com.google.guava:guava:29.0-jre'

    // Use JUnit test framework
    testImplementation 'junit:junit:4.13'
    testImplementation 'io.cucumber:cucumber-java:6.6.1'
    testImplementation 'io.cucumber:cucumber-junit:6.6.1'
    testImplementation 'io.rest-assured:rest-assured:4.3.3'
}

Step 3Add Configuration to build.gradle

configurations {
    cucumberRuntime {
        extendsFrom testImplementation
    }
}

Step 4Add Gradle Cucumber Task to build.gradle

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', 'Cucumber_Gradle_Demo.definitions', 'src/test/resources']
        }
    }
}

task getexample() {
    dependsOn assemble, compileTestJava
    doLast {
        javaexec {
            main = "io.cucumber.core.cli.Main"
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
            args = ['--plugin', 'pretty', '--glue', 'Cucumber_Gradle_Demo.definitions', 'src/test/resources/features/', '--tags', '@getexample']
        }
    }
}

task postexample() {
    dependsOn assemble, compileTestJava
    doLast {
        javaexec {
            main = "io.cucumber.core.cli.Main"
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
            args = ['--plugin', 'pretty', '--glue', 'Cucumber_Gradle_Demo.definitions', 'src/test/resources/features/', '--tags', '@postexample']
        }
    }
}

Here, task cucumber will execute all the tests present in the project irrespective of the number of feature files and scenarios within the feature file.

Step 5 Create a feature file under src/test/resources

I have created 2 sample feature files – API_GetExample.feature and API_PostExample.feature.

Below is the API_GetExample.feature

@getexample
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 200 and id <id> and salary <employee_salary> and name "<employee_name>" and age <employee_age> and message "<message>"
 
Examples:
    |id  |employee_salary|employee_name |employee_age  |message                                  |
    |1   |320800         |Tiger Nixon   |61            |Successfully! Record has been fetched.   |
    
      
  @GetAllUsers    
  Scenario Outline: Send a valid Request to get the details of all the users
 
  Given I send a request to the URL to get the details of all the users
  Then the response will return status 200 and message "<message>"
 
Examples:
      |message                                  |
      | Successfully! All records has been fetched.   |

API_PostExample.feature

@postexample
Feature: Validation of POST method
 
  @CreateUser
  Scenario Outline: Send Request to create a user
 
  Given I send a request to the URL to create a new user
  Then the response will return status 200 and name "<employee_name>" and message "<message>"
 
Examples:
    |employee_name |message                                |
    |posttest      |Successfully! Record has been added.   |

Run Test from Command Line

1. Open the command prompt and change directory to the project location .

cd C:\Users\Vibha\Projects\Vibha_Personal\Cucumber_Gradle_Demo

2. All feature files should be in src/test/resources and create Cucumber Runner class as CucumberRunnerTest.
Note:- The Runner class name should end with Test to execute the tests from Command Line

Running all Feature files or Tests from Command Line

Below command will run all the tests present in the project. As you can see, there are 2 feature files – API_GetExample.feature contains 2 scenarios and API_PostExample.feature contains 1 scenario.

gradle cucumber

Below screenshot shows that Task : Cucumber is triggered.

Below screenshot shows that tests are executed and the status of the tests.

Running a Feature file from Command Line

To run a particular feature, create a task – postexample for that feature in the build.gradle as shown in the below example.

task postexample() {
    dependsOn assemble, compileTestJava
    doLast {
        javaexec {
            main = "io.cucumber.core.cli.Main"
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
            args = ['--plugin', 'pretty', '--glue', 'Cucumber_Gradle_Demo.definitions', 'src/test/resources/features/', '--tags', '@postexample']
        }
    }
}

Add this task as a feature tag name and the use it to run the test of that particular feature file.

@postexample
Feature: Validation of POST method

Use the below command to run the tests of API_PostExample.feature.

gradle postexample

Running Scenarios using Tags from Command Line

To execute the tests using tags, we need to add ‘–tags’, “${tags}” in build.gradle

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','--tags', "${tags}",'--glue', 'Cucumber_Gradle_Demo.definitions', 'src/test/resources']
        }
    }
}

Use the below mentioned command to run the tests tagged with tag = GetUserDetails.

gradle cucumber -P tags=@GetUserDetails