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:-
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 –
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:
Launch the Eclipse IDE and, from the Help menu, click “Install New Software”.
2. You will see a dialog window, click the “Add” 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 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.
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
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!!
In this tutorial, I will explain creating a framework for the testing of Web Application in Cucumber BDD.
Pre-Requisite
Java 11 installed
Gradle installed
Eclipse or IntelliJ installed
This framework consists of:
Serenity – 2.6.0
Serenity Cucumber – 2.6.0
Java 11
JUnit – 4.13.2
Gradle – 7.2
Steps to setup Gradle Java Project for Web Application using Serenity , Cucumber6 and JUnit4
Download and Install Java on the system
Download and setup Eclipse IDE on the system
Setup Gradle on System and create a new Gradle Project
Update repositories, plugin and dependencies to the Gradle project
Create a feature file under src/test/resources
Create the Step Definition class or Glue Code for the Test Scenario
Create a Serenity Cucumber Runner class
Create serenity.conf file under src/test/resources
Create serenity.properties file in the root of the project
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
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.
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.
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.
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!!
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);
}
}
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.
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.
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:
Springboot – 2.5.2
Cucumber – 6.10.4
Java 11
JUnit – 4.13.2
Maven – 3.8.1
RestAssured – 4.3.3
Junit Vintage Engine (To run the tests through commandline)
Steps to setup Cucumber Test Automation Framework for API Testing using Rest-Assured
Add SpringbootTest, Rest-Assured, JUnit and Cucumber dependencies to the project
Create a directory src/test/resources and create a feature file under src/test/resources
Create the Step Definition class or Glue Code for the Test Scenario under src/test/java
Create a Cucumber Runner class under src/test/java
Run the tests from JUnit
Run the tests from Command Line
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;
}
}
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
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 Scenariounder 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:-
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:-
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:-
The assertions are imported from the Hamcrest package:-
import static org.hamcrest.Matchers.*;
Step 4 – Create a Cucumber Runner classunder 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:
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!!
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
Create a Gradle Java Project.
Add Rest-Assured and Cucumber dependencies to the Gradle project
Add Configuration to build.gradle
Add Gradle Cucumber Task to build.gradle
Create a feature file under src/test/resources
Create the Step Definition class or Glue Code for the Test Scenario
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'
}
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.