How to rerun failed tests in Cucumber

HOME

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 such as network issue or Database down and soon. A few tests may fail for no apparent reason and then rerun successfully. We are sometimes required to run only failed test cases after bug fixes in order to validate 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 that generates a file which contains the information about the failed tests.

The Cucumber Framework with Selenium and TestNG can be found here. Use this tutorial to setup the project.

Now, let us add 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 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 = {"rerun:target/failedrerun.txt"})
   
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
   
}

Create Second Runner Class

The next step is to run failed test scenarios present in the text file. We need to create a class same as our runner class which will contain the location of the file which we want to execute to rerun our failed scenarios. In the ‘features’ variable, you need mention the failedrerun.txt file and don’t forget that you must mention ‘@’ symbol before the file path.

import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
   
@CucumberOptions(tags = "", features = {"@target/failedrerun.txt"}, glue = {"com.example.definitions"},
                 plugin = {})
   
public class FailedRunnerTests extends AbstractTestNGCucumberTests {
   
}

Run the tests using below mentioned command

mvn clean test

After running the tests from commandline, first all the tests will be executed. If any test fails, a failedrerun.txt file will be generated that contains the details about the failed tests.

In the below screenshot, we can see that a scenario starting at line 7 is failed.

Once the first round of execution ends, Cucumber Runner go to second runner and run the failed tests that are mentioned in failedrerun.txt.

We can see that 2 seperate reports are generated here.

The first Cucumber Report shows that out of 5 tests, 1 test is failed.

The second Cucumber Report shows that the one failed test is rerun again and it again failed.

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

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

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

Parallel Testing in Cucumber with TestNG

HOME

In this tutorial, I will explain Parallel Testing using Cucumber with TestNG.

Cucumber-JVM allows parallel execution across multiple threads since version 4.0.0. There are several options to incorporate this built-in feature in a Cucumber project. You can do so by using JUnit, TestNG or CLI.

Cucumber can be executed in parallel using TestNG and Maven test execution plugins by setting the dataprovider parallel option to true.

In TestNG the scenarios and rows in a scenario outline are executed in multiple threads. One can use either Maven Surefire or Failsafe plugin for executing the runners. In this tutorial, I’m using the Maven Surefire plugin.

Pre-Requisite

  1. Java is installed
  2. Maven is installed
  3. TestNG is installed

This framework consists of:

  1. Selenium – 3.141.59
  2. Java 11
  3. Cucumber Java – 6.8.1
  4. Cucumber TestNG- 6.8.1
  5. Maven – 3.8.1
  6. TestNG – 7.4.0
  7. Maven Surefire Plugin – 3.0.0-M5

Steps to create a project for parallel Testing in Cucumber

  1. Create a Maven project.
  2. Update Properties section in Maven pom.xml
  3. Add Cucumber and TestNG dependencies to the project.
  4. Add Surefire plugin configuration to the build section to the POM.
  5. Create 2 feature files under src/test/resources – Login.feature and HomePage.feature.
  6. Create the Step Definition classes for both feature files or Glue Code.
  7. Create a Cucumber TestNG Runner class.
  8. Report Generation
  9. Execute the test from Command Line.
  10. Execute the test from TestNG Runner.
  11. Difference between parallel and non parallel tests

Detailed Step Description

Step 1 – Create a Maven project

Create a Maven project in your favorite IDE using the cucumber-archetype. To know more about this, click here.

Step 2 – 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>6.8.1</cucumber.version>
   <testng.version>7.4.0</testng.version>
   <maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
 </properties>

Step 3 – Add Cucumber, Selenium and TestNG dependencies to the project

Add below mentioned Cucumber-Java and Cucumber-TestNG and Selenium-java dependencies to the project.

<dependencies>

    <!--Cucumber Dependencies -->
    <dependency>
      <groupId>io.cucumber</groupId>
      <artifactId>cucumber-java</artifactId>
      <version>${cucumber.version}</version>
    </dependency>
  
    <dependency>
      <groupId>io.cucumber</groupId>
      <artifactId>cucumber-testng</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>

    <!-- TestNG Dependency -->
    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>${testng.version}</version>
      <scope>test</scope>
    </dependency>  

</dependencies>
  

Step 4 – Add Surefire plugin configuration to the build section to the POM

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>${maven.surefire.plugin.version}</version>
</plugin>

Step 5 – Create 2 feature files under src/test/resources – Login.feature and HomePage.feature

Below are the sample feature files.

Login.feature

Feature: Login to HRM Application 
 
   @ValidCredentials
   Scenario: Login with valid credentials - Feature 1, Scenario -1
     
    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

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  - Feature 2, Scenario -1
    
   When User is in Dashboard page
	 Then there are valid QuickLaunch options '<options>'
	  	
	Examples: 
		|options                  |
		|Assign Leave             |
		|Leave List               |

	
	@ValidLegendOptions		
	Scenario Outline: Login with valid credentials to check Manu Options - Feature 2, Scenario -2
    
   When User is in Dashboard page
	 Then there are valid Legend options '<legendOptions>'
	  	
	Examples: 
		|legendOptions               |
		|Administration              |

Step 6 – Create the Step Definition classes for both feature files or Glue Code.

Below is the Step Definition for Login.feature.

public class LoginDefinition {

	WebDriver driver;

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

		System.out.println("Thread ID - " + Thread.currentThread().getId());
		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);

		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");
		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");
		driver.findElement(By.name("txtPassword")).sendKeys(passWord);

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

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

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

		driver.quit();

	}

Below is the Step Definition for HomePage.feature.

public class HomePageDefinitions {

	WebDriver driver;

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

		System.out.println("Thread ID - " + Thread.currentThread().getId());
		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);
		driver.get("https://opensource-demo.orangehrmlive.com/");
	}

	@And("User login with valid credentials")
	public void entersCredentials() throws InterruptedException {
		Thread.sleep(1000);

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

	}

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

		String dashboardTitle = driver.findElement(By.id("welcome")).getText();
		assertThat(dashboardTitle, containsString("Welcome"));

	}

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

		switch (options) {
		case "Assign Leave":
			String linkOne = driver
					.findElement(By.xpath(
							"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[1]/div/a/span"))
					.getText();
			Assert.assertEquals(linkOne, options);
			Thread.sleep(1000);
			break;
		case "Leave List ":
			String linkTwo = driver
					.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 = driver
					.findElement(By.xpath(
							"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[3]/div/a/span"))
					.getText();
			Assert.assertEquals(linkThree, options);
			Thread.sleep(1000);
			break;
		default:
			break;
		}

		driver.quit();

	}

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

		switch (options) {
		case "Not assigned to Subunits":
			String linkOne = driver
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[1]/td[2]"))
					.getText();
			Assert.assertEquals(linkOne, options);
			Thread.sleep(1000);
			break;
		case "Administration":
			String linkTwo = driver
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[2]/td[2]"))
					.getText();
			Assert.assertEquals(linkTwo, options);
			Thread.sleep(1000);
			break;
		case "Client Services":
			String linkThree = driver
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[3]/td[2]"))
					.getText();
			Assert.assertEquals(linkThree, options);
			Thread.sleep(1000);
			break;
		default:
			break;

		}
		driver.quit();
	}
}

Step 7 – Create a Cucumber TestNG Runner class

Add a cucumber runner by extending the AbstractTestNGCucumberTests class and overriding the scenarios method. Set the parallel option value to true for the DataProvider annotation.

import org.testng.annotations.DataProvider;

import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;

@CucumberOptions(tags = "", features = "src/test/resources/features", glue = "com.example.Cucumber_TestNGDemo.definitions")

public class CucumberRunnerTests extends AbstractTestNGCucumberTests {

	@Override
	@DataProvider(parallel = true)
	public Object[][] scenarios() {
		return super.scenarios();
	}

}

Step 8 – Report Generation

Add cucumber.properties under src/test/resources and add the below instruction in the file.

cucumber.publish.enabled=true

Step 9 – Execute the test from Command Line

Use the below mentioned command in command prompt to run the tests.

mvn test

Step 10 – Execute the tests from TestNG Runner

Go to Runner class and right click Run As TestNG Test. The tests will run as TestNG tests.

Step 11 – Difference between parallel and non parallel tests

Parallel Tests – All the tests are started at the same time, so they share different threads. The way tests are executed are different in them. With non-paralle tests, all the scenarios of the same feature are executed together and then he scenarios of another feature file. Whereas, in parallel test, all the tests are started at the same time, so there won’t be any specific order.

All the scenarios and rows of the scenario outlines are executed in different threads.

Non Parallel Tests

Parallel Test Report – The execution time to complete the tests are less in parallel test execution as compared to the non parallel one.

Non Parallel Tests Report

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

There is another tutorial which shows Parallel Testing in Cucumber with JUnit.

Integration of Cucumber with Selenium and TestNG

HOME

Cucumber is a BDD Tool, and Selenium WebDriver is used for the automation of web applications. Imagine we need to build a test framework that can be used by businesses to understand the test scenarios and as well can 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.

In this tutorial, I’ll create a BDD Framework for the testing of web applications using Selenium WebDriver with TestNG. This framework consists of:-

  1. Cucumber Java- 7.6.0
  2. Cucumber TestNG – 7.6.0
  3. Java 11
  4. TestNG – 7.4.0
  5. Maven – 3.8.6
  6. Selenium – 4.3.0

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

  1. Download and Install Java on the system
  2. Download and setup Eclipse IDE on the system
  3. Setup Maven
  4. Install Cucumber Eclipse Plugin (For Eclipse IDE)
  5. Create a new Maven Project
  6. Create source folder – src/test/resources to create test scenarios in the Feature file
  7. Add Selenium, TestNG, and Cucumber dependencies to the project
  8. Add Maven Compiler Plugin and Maven Surefire Plugin
  9. Create a feature file under src/test/resources
  10. Create the Step Definition class or Glue Code in src/test/java
  11. Create a TestNG Cucumber Runner class in src/test/java
  12. Run the tests from TestNG Runner
  13. Run the tests from TestNG.xml
  14. Run the tests from Command Line
  15. Cucumber Report Generation
  16. TestNG Report Generation

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, 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.

Step 4 – Install Cucumber Eclipse Plugin (Only for Eclipse IDE)

The Cucumber Eclipse plugin is an plugin that allows the eclipse to understand the Gherkin syntax. The Cucumber Eclipse Plugin highlights the keywords present in Feature File. Click here to know more – Install Cucumber Eclipse Plugin

Step 5 – Create a new Maven Project

Click here to know How to create a Maven project

Below is the Maven project structure. Here,

Group Id – com.example
Artifact Id – CucumberTestNG_Demo
Version – 0.0.1-SNAPSHOT
Package – com. example. CucumberTestNG_Demo

Step 6 – 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 7 – Add Selenium, TestNG, and Cucumber dependencies to the project

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

  <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <cucumber.version>7.6.0</cucumber.version>
        <selenium.version>4.3.0</selenium.version>
        <webdrivermanager.version>5.2.1</webdrivermanager.version>
        <testng.version>7.4.0</testng.version>
        <apache.common.version>2.4</apache.common.version>     
        <maven.compiler.plugin.version>3.10.1</maven.compiler.plugin.version>
        <maven.surefire.plugin.version>3.0.0-M7</maven.surefire.plugin.version>
        <maven.compiler.source.version>11</maven.compiler.source.version>
        <maven.compiler.target.version>11</maven.compiler.target.version>
    </properties>
 
    <dependencies>
 
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>${cucumber.version}</version>
        </dependency>
 
        <dependency>
           <groupId>io.cucumber</groupId>
           <artifactId>cucumber-testng</artifactId>
           <version>${cucumber.version}</version>
           <scope>test</scope>
       </dependency>
         
        <!-- Selenium -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
        </dependency>
 
        <!-- Web Driver Manager -->
        <dependency>
            <groupId>io.github.bonigarcia</groupId>
            <artifactId>webdrivermanager</artifactId>
            <version>${webdrivermanager.version}</version>
        </dependency>
 
        <!-- TestNG -->
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
            <scope>test</scope>
        </dependency>
 
        <!-- Apache Common -->
        <dependency>
            <groupId>org.apache.directory.studio</groupId>
            <artifactId>org.apache.commons.io</artifactId>
            <version>${apache.common.version}</version>
        </dependency>
       
    </dependencies>

Step 8 – Add Maven Compiler Plugin and SureFire Plugin

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

  • compile – compile main source files
  • testCompile – compile test source files
 <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.version}</version>
                <configuration>
                    <source>${maven.compiler.source.version}</source> 
                    <target>${maven.compiler.target.version}</target> 
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven.surefire.plugin.version}</version>
                <configuration>
                    <suiteXmlFiles>
                        <suiteXmlFile>testng.xml</suiteXmlFile>
                    </suiteXmlFiles>
                </configuration>
            </plugin>
        </plugins>
    </build>

If you don’t add compiler plugin to the POM.xml and when you will try to run the tests through Maven, then the build will fail with the below message.

The complete POM.xml is shown below.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>CucumberTestNG_Demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>


   <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <cucumber.version>7.6.0</cucumber.version>
        <selenium.version>4.3.0</selenium.version>
        <webdrivermanager.version>5.2.1</webdrivermanager.version>
        <testng.version>7.4.0</testng.version>
        <apache.common.version>2.4</apache.common.version>     
        <maven.compiler.plugin.version>3.10.1</maven.compiler.plugin.version>
        <maven.surefire.plugin.version>3.0.0-M7</maven.surefire.plugin.version>
        <maven.compiler.source.version>11</maven.compiler.source.version>
        <maven.compiler.target.version>11</maven.compiler.target.version>
    </properties>
 
    <dependencies>
 
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>${cucumber.version}</version>
        </dependency>
 
        <dependency>
           <groupId>io.cucumber</groupId>
           <artifactId>cucumber-testng</artifactId>
           <version>${cucumber.version}</version>
           <scope>test</scope>
       </dependency>
         
        <!-- Selenium -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
        </dependency>
 
        <!-- Web Driver Manager -->
        <dependency>
            <groupId>io.github.bonigarcia</groupId>
            <artifactId>webdrivermanager</artifactId>
            <version>${webdrivermanager.version}</version>
        </dependency>
 
        <!-- TestNG -->
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
            <scope>test</scope>
        </dependency>
 
        <!-- Apache Common -->
        <dependency>
            <groupId>org.apache.directory.studio</groupId>
            <artifactId>org.apache.commons.io</artifactId>
            <version>${apache.common.version}</version>
        </dependency>
       
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.version}</version>
                <configuration>
                    <source>${maven.compiler.source.version}</source> 
                    <target>${maven.compiler.target.version}</target> 
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven.surefire.plugin.version}</version>
                <configuration>
                    <suiteXmlFiles>
                        <suiteXmlFile>testng.xml</suiteXmlFile>
                    </suiteXmlFiles>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
Step 9 – Create a feature file (LoginPage.feature) containing all the test scenarios under src/test/resources/features

It is recommended to create a features folder in 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 10 – Create the stepdefinition class corresponding to the feature file to test the scenarios under src/test/java

It is recommended to create a definitions folder in src/test/java directory. The StepDefinition files should be created in this definitions directory. within the folder called definitions.

import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
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;

public class LoginPageDefinitions {
	
	private static WebDriver driver;	   
	public final static int TIMEOUT = 10;
	
	@Before
    public void setUp() {

		WebDriverManager.chromedriver().setup();
	    driver = new ChromeDriver();
	    driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
	    driver.manage().window().maximize();
    }

	@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
        
    }
    
    @Then("User should be able to login sucessfully and new page open")
    public void verifyLogin() {
 
    	 String homePageHeading = driver.findElement(By.xpath("//*[@id='app']/div[1]/div[2]/div[2]/div/div[1]/div[1]/div[1]/h5")).getText();
    	 
    	 //Verify new page - HomePage
         Assert.assertEquals(homePageHeading,"Employee Information");
 
    }
    
    @Then("User should be able to see error message {string}")
    public void verifyErrorMessage(String expectedErrorMessage) {
 
    	String actualErrorMessage = driver.findElement(By.xpath("//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/div/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:-

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;

Step 11 – Create a TestNG Cucumber Runner class to execute the test scenarios in 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.

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 12 – Test Execution through TestNG

Go to 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 13 – Run the tests from TestNG.xml

Create a TestNG.xml as shown below and run the tests as TestNG.

<?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 14 – Run the tests from Command Line

Run the below command in the command prompt to run the tests and to get the test execution report.

mvn test

The execution screen looks like something as shown below.

Step 15 – Cucumber Report Generation

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.

Step 16 – TestNG Report Generation

TestNG generates the various types of reports under test-output folder like emailable-report.html, index.html, testng-results.xml.

We are interested in the’emailable-report.html’ report. Open “emailable-report.html”, as this is an HTML report, and open it with the browser. The below image shows emailable-report.html.

TestNG also produce “index.html” report, and it resides under test-output folder. The below image shows index.html report.

If you like to use Cucumber with Page Object Model, please refer to this tutorial.

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

How To Create Gradle Project with Cucumber to test Rest API

HOME

This tutorial describes the creation of the Gradle Java Project to test Rest API using Cucumber BDD and Rest-Assured.

In this tutorial, I will explain creating a framework for the testing of Rest API in Cucumber BDD. This framework consists of:

  1. Cucumber – 6.8.1 or above
  2. Java 8 or above
  3. JUnit 4
  4. Gradle 6.6.1 (Build Tool)
  5. Rest Assured 4.3.3

Steps to set up Gradle Java Project for API Testing using Rest-Assured and Cucumber

  1. Download and Install Java on the system
  2. Download and setup Eclipse IDE on the system
  3. Setup Gradle on System
  4. Create a new Gradle Project
  5. Add Rest-Assured and Cucumber dependencies to the Gradle project
  6. Add Configuration to build.gradle
  7. Add Gradle Cucumber Task to build.gradle
  8. Create a feature file under src/test/resources
  9. Create the Step Definition class or Glue Code for the Test Scenario
  10. Create a Cucumber Runner class
  11. Run the tests from JUnit
  12. Run the tests from Command Line
  13. Cucumber Report Generation

Project Structure

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 Gradle

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

Step 4 – Create a new Gradle Project

To create a new Gradle project, go to the top left side and select File -> New Project -> Gradle -> Gradle project -> Next -> Enter Project Name and Project Location ->Next ->Select Gradle Version ->Next ->Review the Configuration -> Finish.

Click here to know How to create a Gradle Java project. Below is the structure of the Gradle project.

Step 5 – Add Rest-Assured and Cucumber dependencies to the Gradle project

This syntax is used for Gradle 5.0 and higher.

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'
    
    testImplementation 'io.cucumber:cucumber-java:6.8.1'
    testImplementation 'io.cucumber:cucumber-junit:6.8.1'
    testImplementation 'io.rest-assured:rest-assured:4.3.3'

If you are using Gradle 4.10.3 or older, use the below dependency block to build.gradle.

dependencies {
    testCompile 'io.cucumber:cucumber-java:6.8.1'
}

Step 6 – Add Configuration to build.gradle

The below configuration is added to build.gradle when Gradle is 5.0 or higher version.

configurations {
    cucumberRuntime {
        extendsFrom testImplementation
    }
}

If Gradle is 4.10.3 or older, use the below configuration.

configurations {
    cucumberRuntime {
        extendsFrom testRuntime
    }
}

Step 7 – Add 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', 'com.example.gradle.apidemo', 'src/test/resources']
        }
    }
}

Once you have added dependencies, configurations, and Gradle cucumber task, Right-click on the project, Hover to the Gradle option, and click Refresh Gradle Project. Eclipse does not automatically update the class path if the build.gradle file is updated. Select Gradle  Refresh Gradle Project from the context menu of the project or from your build.gradle file for that.

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

A new Gradle Project is created with 4 folders – src/main/java, src/main/resources, src/test/java and src/test/resources. Features are created under the src/test/resources directory. Create a folder with name features. Now, create the feature file in this folder. The feature file should be saved with extension .feature. This feature file contains the test scenarios created to test the application. The Test Scenarios are written in Gherkins language in the format of Given, When, Then, And, But.

Below is an example of 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 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.   |
   

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

Step Definition 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 matched glue or test code.

import io.restassured.http.ContentType;
 
import io.restassured.response.ValidatableResponse;
 
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
 
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
 
public class API_GETDefinitions {
     
 
    private ValidatableResponse validatableResponse;
 
    private String endpoint = "http://dummy.restapiexample.com/api/v1/employee/1";
  
    @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 salary {int} and name {string} and age {int} and message {string}")
    public void verifyStatus(int statusCode, int id, int emp_Salary, String emp_name, int emp_age, String message ){
         
        validatableResponse.assertThat().statusCode(statusCode);
         
        validatableResponse.assertThat().body("data.id",equalTo(id));
         
        validatableResponse.assertThat().body("data.employee_salary",equalTo(emp_Salary));
         
        validatableResponse.assertThat().body("data.employee_name",equalTo(emp_name));
         
        validatableResponse.assertThat().body("data.employee_age",equalTo(emp_age));
         
        validatableResponse.assertThat().body("message",equalTo(message));      
         
    }
}

In order to use REST assured effectively, it’s recommended to statically import methods from the following classes:

import io.restassured.RestAssured.*
import io.restassured.matcher.RestAssuredMatchers.*
import static org.hamcrest.Matchers.*

given() method is imported from package:

import static io.restassured.RestAssured.given;

equalTo() method is imported from package:

import static org.hamcrest.Matchers;

Step 10 – Create a Cucumber Runner class 

A runner will help us to run the feature file and acts as an interlink between the feature file and step definition Class. To know more about Runner, refer to this link.

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/API_GET.feature"}, glue= {"com.example.gradle.apidemo"})

public class CucumberRunnerTest {

}

Step 11 – Run the tests from JUnit

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

Step 12 – Run the tests from Command Line

Run the following Gradle task from the directory path where build.gradle file is located. To know more about this report, refer here.

gradle cucumber

Below is the screenshot of the execution of Cucumber tests in Command-Line.

Step 13 – Cucumber Report Generation

To get Cucumber Test Reports, add cucumber.properties under src/test/resources and add the below instruction in the file.

cucumber.publish.enabled=true

Below is the image of the Cucumber Report.

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

Rest API Test in Cucumber BDD

HOME

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 for API Testing.

What is Rest Assured?

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

Framework

In this tutorial, I will explain creating a framework for the testing of Rest API in Cucumber BDD. This framework consists of:

  1. Cucumber – 6.8.1
  2. Java 8 or above
  3. JUnit – 4.13.2
  4. Maven
  5. Rest Assured – 4.3.3

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

  1. Download and Install Java on the system
  2. Download and setup Eclipse IDE on the system
  3. Setup Maven on System
  4. Create a new Maven Project
  5. Install the Cucumber Eclipse plugin for the Eclipse project
  6. Create a source folder – src/test/resources to create test scenarios in the Feature file
  7. Add Rest-Assured and Cucumber dependencies to the project
  8. Add Maven Compiler Plugin
  9. Create a feature file under src/test/resources
  10. Create the Step Definition class or Glue Code for the Test Scenario
  11. Create a Cucumber Runner class
  12. Run the tests from JUnit
  13. Run the tests from Command Line
  14. Cucumber Report Generation

Project Structure

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

Click here to know How to create a Maven project

Step 5 – Install the Cucumber Eclipse plugin for the Eclipse project

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

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

<!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <version>4.3.3</version>
    <scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-junit -->
<dependency>
      <groupId>io.cucumber</groupId>
      <artifactId>cucumber-junit</artifactId>
      <version>6.8.1</version>
      <scope>test</scope>
</dependency>
  
<dependency>
	  <groupId>io.cucumber</groupId>
	  <artifactId>cucumber-java</artifactId>
	  <version>6.8.1</version>
	  <scope>test</scope>
</dependency>

<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
</dependency>

Step 8 – Add Maven Compiler Plugin

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

  • compile – compile main source files
  • testCompile – compile test source files
<build>
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>3.7.0</version>
         <configuration>
           <source>1.8</source>
           <target>1.8</target>
            <encoding>UTF-8</encoding>          
         </configuration>
       </plugin>                
     </plugins>
</build>

The complete POM.xml will look like something below:

<dependencies>
      <!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
      <dependency>
         <groupId>io.rest-assured</groupId>
         <artifactId>rest-assured</artifactId>
         <version>4.3.3</version>
         <scope>test</scope>
      </dependency>
      <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-junit -->
      <dependency>
         <groupId>io.cucumber</groupId>
         <artifactId>cucumber-junit</artifactId>
         <version>6.8.1</version>
         <scope>test</scope>
      </dependency>
      <dependency>
         <groupId>io.cucumber</groupId>
         <artifactId>cucumber-java</artifactId>
         <version>6.8.1</version>
         <scope>test</scope>
      </dependency>
      <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.12</version>
         <scope>test</scope>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.7.0</version>
            <configuration>
               <source>1.8</source>
               <target>1.8</target>
               <encoding>UTF-8</encoding>
            </configuration>
         </plugin>
      </plugins>
   </build>
</project>
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 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.   |
   

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 matched glue or test code.

import io.restassured.http.ContentType;

import io.restassured.response.ValidatableResponse;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;

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

public class API_GETDefinitions {
	

	private ValidatableResponse validatableResponse;

    private String endpoint = "http://dummy.restapiexample.com/api/v1/employee/1";
 
	@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 salary {int} and name {string} and age {int} and message {string}")
	public void verifyStatus(int statusCode, int id, int emp_Salary, String emp_name, int emp_age, String message ){
		
		validatableResponse.assertThat().statusCode(statusCode);
		
		validatableResponse.assertThat().body("data.id",equalTo(id));
		
		validatableResponse.assertThat().body("data.employee_salary",equalTo(emp_Salary));
		
		validatableResponse.assertThat().body("data.employee_name",equalTo(emp_name));
		
		validatableResponse.assertThat().body("data.employee_age",equalTo(emp_age));
		
		validatableResponse.assertThat().body("message",equalTo(message));		
		
	}
}

In order to use REST assured effectively it’s recommended to statically import methods from the following classes:

io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
org.hamcrest.Matchers.*

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 salary {int} and name {string} and age {int} and message {string}")
	public void verifyStatus(int statusCode, int id, int emp_Salary, String emp_name, int emp_age, String message ){
				

validatableResponse.assertThat().statusCode(statusCode).body("data.id",equalTo(id)).and()
		.body("data.employee_salary",equalTo(emp_Salary)).body("data.employee_name",equalTo(emp_name))
		.body("data.employee_age",equalTo(emp_age)).body("message",equalTo(message));

Step 11 – Create a JUnit Cucumber Runner class to execute the test scenarios

A runner will help us to run the feature file and acts as an interlink between the feature file and StepDefinition Class.

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.example.apidemo"})
public class CucumberRunnerTest {

}

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.

Step 13 – Run the tests from Command Line

Run the below command in the command prompt to run the tests and to get the test execution report.

mvn test

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

Conditional Hooks in Cucumber

HOME

In the previous tutorial, I explained Hooks in Cucumber. In this tutorial, I will explain Condition Hooks in Cucumber.

Hooks can be conditionally selected for execution based on the tags of the scenario. These are called Condition or Tagged Hooks.

Tagged Hooks are much like the scenario hooks, but the only difference is that they are executed before and after the specified tag.

These Tagged hooks will be very specific to the particular tags, so these are not common for all scenarios.

So basically, they can also be run in the following two ways:

  • Before (‘tagName’)
  • After (‘tagName’)

Why do we need Tagged Hooks?

Suppose there are 3 different sets of scenarios. The prerequisites of these scenarios are different, like they have to open different browsers. So, we don’t want to have a common hook for all the scenarios. In this case, we can create a tagged hook to satisfy the requirement of each scenario.

In the below example, there are 3 tags – ChromeBrowser, FireFoxBrowser, and EdgeBrowser. I want to run the hook which has the specified tag for that scenario. For Example, I want @After and @Before hooks related to Chrome Browser should be executed for tag – @ChromeBrowser.

Below is the feature file which has 3 different scenarios.

Feature: Conditional or Tagged Hooks
 
@ChromeBrowser
Scenario: Open Chrome Browser
 
Given I want to open Google Chrome Browser

@FireFoxBrowser
Scenario: Open Firefox Browser
 
Given I want to open Mozilla Firefox Browser

@EdgeBrowser
Scenario: Open Edge Browser
 
Given I want to open Microsoft Edge Browser

Below is the Step Definition for the above feature file.

package com.Cucumber;

import cucumber.api.java.en.Given;

public class ConditionalHooksExampleDefinitions {
	
	@Given("I want to open Google Chrome Browser")
	public void chrome() throws Throwable {
	    System.out.println("I want to open Google Chrome Browser");
	}

	@Given("I want to open Mozilla Firefox Browser")
	public void firefox() throws Throwable {
		System.out.println("I want to open Mozilla Firefox Browser");
	}

	@Given("I want to open Microsoft Edge Browser")
	public void edge() throws Throwable {
		System.out.println("I want to open Microsoft Edge Browser");
	}

}

Hooks can be defined in the same class or different. I have defined Hooks in a separate class.

package com.Cucumber;

import cucumber.api.java.After;
import cucumber.api.java.Before;

public class Hooks {
	
	@BeforeStep
    public void beforeStep(){
        System.out.println("@@@@@@@@@@@@@@@ Before Step @@@@@@@@@@@@@@@@@");
    }
	
	@AfterStep
    public void afterStep(){
        System.out.println("@@@@@@@@@@@@@@@@  After Step @@@@@@@@@@@@@@@@");
    }
	
	@Before("@ChromeBrowser")
    public void beforeGoogle(){
        System.out.println("******* before Chrome *******");
    }
	
	@Before("@FireFoxBrowser")
    public void beforeFirefox(){
        System.out.println("$$$$$$$$$$ before FireFox $$$$$$$$$$");
    }

	@Before("@EdgeBrowser")
    public void beforeEdge(){
		System.out.println("============ before Edge =============");
    }
	
	@After("@ChromeBrowser")
    public void afterGoogle(){
        System.out.println("********* After Google *********");
    }

	@After("@FireFoxBrowser")
    public void afterFireFox(){
		System.out.println("$$$$$$$$$$$ After FireFox $$$$$$$$$$");
    }

	@After("@EdgeBrowser")
    public void afterEdge(){
        System.out.println("============ After Edge ==============");
    }

}

There is no change in the Test Runner Class.

package com.Cucumber;

import org.junit.runner.RunWith;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;

@RunWith(Cucumber.class)
@CucumberOptions(monochrome = true, plugin = "pretty", features = "src/test/resources/features/ConditionalHooksExample.feature", tags = {
		"" })
public class MyRunner {

}

The execution Result looks like something below

  1. At the start of execution, @Before(“@ChromeBrowser”) {Scenario Hook} is executed.
  2. After that @BeforeStep (Step Hook) hook is executed.
  3. The given statement of the @ChromeBrowser tag is executed in the third step.
  4. The fourth step is to execute @AfterStep.
  5. Now, at the last, the @After(“@ChromeBrowser”) hook is executed. Similarly, the same sequence is followed for FireFox and Edge Scenarios.

Parallel Testing in Cucumber with JUnit

HOME

In this tutorial, I will explain Parallel Testing using Cucumber with JUnit.

Cucumber-JVM allows parallel execution across multiple threads since version 4.0.0. There are several options to incorporate this built-in feature in a Cucumber project. You can do so by using JUnit, TestNG, or CLI.

Cucumber can be executed in parallel using JUnit and Maven test execution plugins.

In JUnit, the feature files are run in parallel rather than scenarios, which means all the scenarios in a feature file will be executed by the same thread. You can use either Maven Surefire or Failsafe plugin to execute the runner. In this tutorial, I’m using the Maven Surefire plugin.

This framework consists of:-

  1. Cucumber Java- 6.8.1
  2. Cucumber JUnit – 6.8.1
  3. Java 11
  4. JUnit– 4.13.2
  5. Maven – 3.8.1
  6. Selenium – 3.141.59
  7. Maven Surefire Plugin – 3.0.0-M5

Steps to create a project for parallel Testing in Cucumber

  1. Create a Maven project.
  2. Add Cucumber and JUnit dependencies to the project.
  3. Add Surefire plugin configuration to the build section to the POM.
  4. Create a feature file under src/test/resources.
  5. Create the Step Definition class or Glue Code.
  6. Create a Cucumber Runner class.
  7. Execute the test from Command-Line.
  8. Generate Cucumber Report.

Detailed Step Description

Step 1 – Create a Maven project

Create a Maven project in your favorite IDE using the cucumber-archetype or by adding Cucumber dependencies to the POM as detailed here and Junit dependencies here. To know more about this, click here.

Below is the structure of the project.

Step 2 – Update the Properties section in Maven pom.xml

   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <selenium.version>3.141.59</selenium.version>
      <cucumber.version>6.8.1</cucumber.version>
      <junit.version>4.13.2</junit.version>
      <webdrivermanager.version>5.1.0</webdrivermanager.version>
      <maven.compiler.plugin.version>3.7.0</maven.compiler.plugin.version>
      <maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
   </properties>

Step 2 – Add Cucumber and JUnit dependencies to the project

Add below mentioned Cucumber-Java and Cucumber-JUnit dependencies to the project.

<!-- 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>
      
      <!-- JUnit Dependency -->
      <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>${junit.version}</version>
         <scope>test</scope>
      </dependency>
      
      <dependency>
         <groupId>org.seleniumhq.selenium</groupId>
         <artifactId>selenium-java</artifactId>
         <version>${selenium.version}</version>
      </dependency>
      
      <dependency>
         <groupId>io.github.bonigarcia</groupId>
         <artifactId>webdrivermanager</artifactId>
         <version>${webdrivermanager.version}</version>
      </dependency>

   </dependencies>
  

Step 3 – Add Surefire plugin configuration to the build section of the POM.

 <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>${maven.surefire.plugin.version}</version>
   <configuration>
        <parallel>methods</parallel>
        <useUnlimitedThreads>true</useUnlimitedThreads>
        <testFailureIgnore>true</testFailureIgnore>
	</configuration>
</plugin> 

To set the thread count to a specific number instead of useUnlimitedThreads use the below setting.

<configuration>
    <parallel>methods</parallel>
    <threadCount>4</threadCount>
</configuration>

The thread count in the above setting is 4 threads per core.

Step 4 – Create a feature folder under src/test/resources

Add 4 feature files – FlightBooking.feature, TrainBooking.feature, HomePage.feature and Login.feature

FlightBooking.feature

Feature: Book flight ticket 
 
@BookOneWayFlight
Scenario: Book Flight for one way trip
 
Given I want to book one way flight ticket from Dublin to London on 22nd July for 2 adults and 2 kids
Then TripAdvisor should provide me options to book flight ticket

@BookRoundTripFlight
Scenario: Book Flight for round trip
 
Given  I want to book roundtrip flight ticket from Dublin to India on 30th June and return 10th July for 2 adults and 1 kid
Then TripAdvisor should provide me options to book flight ticket 

TrainBooking.feature

Feature: Book Train Ticket
 
@BookOneWayTrain
Scenario: Book train ticket for one way
 
Given I want to book one way train ticket from Dublin to Cork on 10th June for 2 adults and 2 kids
Then IrishRail should provide me options to book train ticket for the specified date

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  - Feature 2, Scenario -1
     
   When User is in Dashboard page
     Then there are valid QuickLaunch options '<options>'
         
    Examples: 
        |options                  |
        |Assign Leave             |
        |Leave List               |
 
     
    @ValidLegendOptions    
    Scenario Outline: Login with valid credentials to check Manu Options - Feature 2, Scenario -2
     
   When User is in Dashboard page
     Then there are valid Legend options '<legendOptions>'
         
    Examples: 
        |legendOptions               |
        |Administration              |

Login.feature

Feature: Login to HRM Application 
  
   @ValidCredentials
   Scenario: Login with valid credentials - Feature 1, Scenario -1
      
    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

Step 5 – Create the Step Definition class or Glue Code

Below is the Step Definition for FlightBooking.feature.

public class FlightBookingDefinitions {
	
	@Given("I want to book one way flight ticket from Dublin to London on 22nd July for 2 adults and 2 kids")
	   public void singleTrip(){
	        System.out.println("I want to book one way flight ticket from Dublin to London on 22nd July for 2 adults and 2 kids");
	   }
	 
	@Then("TripAdvisor should provide me options to book flight ticket")
	   public void TripAdvisor(){
	        System.out.println("TripAdvisor should provide me options to book flight ticket");
	   }
	
	@Given("I want to book roundtrip flight ticket from Dublin to India on 30th June and return 10th July for 2 adults and 1 kid")
	   public void roundTrip(){
	        System.out.println("I want to book one way flight ticket from Dublin to London on 22nd July for 2 adults and 2 kids");
	   }
}

Below is the StepDefinition for TrainBooking.feature.

public class TrainBookingDefinitions {

	@Given("I want to book one way train ticket from Dublin to Cork on 10th June for 2 adults and 2 kids")
	   public void hotelWithoutBreakfast(){
	        System.out.println("I want to book one way train ticket from Dublin to Cork on 10th June for 2 adults and 2 kids");
	   }
	 
	@Then("IrishRail should provide me options to book train ticket for the specified date")
	   public void Trivago(){
	        System.out.println("IrishRail should provide me options to book train ticket for the specified date");
	   }

}

Below is the StepDefinition for Login.feature.

public class LoginDefinition {

	WebDriver driver;

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

		WebDriverManager.firefoxdriver().setup();
		driver = new FirefoxDriver();
		driver.manage().window().maximize();
		driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

		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");
		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");
		driver.findElement(By.name("txtPassword")).sendKeys(passWord);

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

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

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

		driver.quit();

	}
}

Below is the StepDefinition for HomePage.feature.

public class HomePageDefinition {

	WebDriver driver;

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

        WebDriverManager.firefoxdriver().setup();
		driver = new FirefoxDriver();
		driver.manage().window().maximize();
		driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
		driver.get("https://opensource-demo.orangehrmlive.com/");
	}

	@Given("User login with valid credentials")
	public void entersCredentials() throws InterruptedException {
		Thread.sleep(1000);

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

	}

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

		String dashboardTitle = driver.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 = driver
					.findElement(By.xpath(
							"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[1]/div/a/span"))
					.getText();
			Assert.assertEquals(linkOne, options);
			Thread.sleep(1000);
			break;
		case "Leave List ":
			String linkTwo = driver
					.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 = driver
					.findElement(By.xpath(
							"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[3]/div/a/span"))
					.getText();
			Assert.assertEquals(linkThree, options);
			Thread.sleep(1000);
			break;
		default:
			break;
		}

		driver.quit();

	}

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

		switch (options) {
		case "Not assigned to Subunits":
			String linkOne = driver
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[1]/td[2]"))
					.getText();
			Assert.assertEquals(linkOne, options);
			Thread.sleep(1000);
			break;
		case "Administration":
			String linkTwo = driver
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[2]/td[2]"))
					.getText();
			Assert.assertEquals(linkTwo, options);
			Thread.sleep(1000);
			break;
		case "Client Services":
			String linkThree = driver
					.findElement(
							By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[3]/td[2]"))
					.getText();
			Assert.assertEquals(linkThree, options);
			Thread.sleep(1000);
			break;
		default:
			break;

		}
		driver.quit();
	}
}

Step 6 – Create a Test Runner to run 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.parallel_demo"})
public class RunCucumberTest {

}

Step 7 – Execute the tests from the command line

mvn test

Below is the execution screen. There are four feature files. Out of four files, two are simple feature files (FlightBooking and TrainBooking ) whereas another two Feature files are complex (Login and Home). These feature files contain the below number of scenarios.

FlightBooking – 2 Scenarios
TrainBooking – 1 Scenario
Login – 1 Scenario
Home – 4 Scenarios

Feature files are executed in alphabetical order. So, the sequence in which non-parallel tests will run is FlightBooking -> Home ->Login -> TrainBooking

But, as we have executed tests parallelly, so the feature files will run in this order (depending on the least number of scenarios will be executed first) TrainBooking -> FlightBooking -> Login -> Home

All the tests of a particular feature file are executed together as feature files are run in parallel, not scenarios.

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

Parallel Testing with Dependency Injection

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

To perform Dependency Injection in Cucumber, we need to add a cucumber-picocontainer dependency to POM.xml.

<!-- Dependency Injection -->
      <dependency>
         <groupId>io.cucumber</groupId>
         <artifactId>cucumber-picocontainer</artifactId>
         <version>${cucumber.picocontainer.version}</version>
         <scope>test</scope>
      </dependency>

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();

	}

	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() {

		hooks.getDriver().get("https://opensource-demo.orangehrmlive.com/");
	}

	@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"));

	}
}

HomePageDefinition

public class HomePageDefinition {

	ApplicationHooks hooks;

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

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

		hooks.getDriver().get("https://opensource-demo.orangehrmlive.com/");
	}

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

		Thread.sleep(1000);

		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);
			Thread.sleep(1000);
			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);
			Thread.sleep(1000);
			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);
			Thread.sleep(1000);
			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);
			Thread.sleep(1000);
			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);
			Thread.sleep(1000);
			break;
		default:
			break;

		}
	}
}

Now, lets the test execution begins

Let us go to the Cucumber Report which is displayed on the screen.

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

In the next tutorial, I explained Parallel Testing in Cucumber with TestNG.