Step by Step Implementation Cucumber BDD Framework using Java, Selenium, Jenkins, GIT, Extent Report and Allure report-Part 1
-By Gayathri Vimalan
Let’s try to understand first what is BDD? How it helps to build better software:
Do you know when BBD was born?. Earlier TDD( Test Driven Development) was helping to qualify the quality code. It is very simple to describe, but it’s proven hard to teach and hard to learn. That's where BBD comes into Dan North’s mind. So he replaced with “behavior” instead of “test” in which the tests were written in plain English language using a simple Given/When/Then syntax. BDD mainly focuses on the behavior of the system rather than technical specifications. So it gave the organization much confidence that it satisfies end user expectations.
BDD is all about collaboration between Business teams and Technical teams. It generates the common documentation which can be understood by all teams and stakeholders. BDD streamlines the requirements of the process and also brings the value to QA. Because every line of the production code, configuration or even specification is directly linked to its own particular scenario, which leads to the ultimate project business goal. This will end up knowing how much business value each line of our code must deliver to the end user. So Cucumber is one of the software tools that supports BDD. Let’s build a sample project with Cucumber.
Before creating the project in Eclipse. Make sure you have installed Java and Maven and set the environment variables as shown in the following screenshot.
Please make sure you have downloaded the MAVEN plugin from Eclipse. If not please refer below:
Open Eclipse and go to help>Eclipse Marketplace>search maven>Eclipse m2e and install. Please refer to the screenshot below.
Step 1: Setting up on Eclipse
Create a Maven project
Go to File > New > Other
Select Maven> Maven Project and click Next
Then select default Workspace location and click Next
Select Catalog and Group Id and click Next
Now we have to give a name for our project. Here I have given the same name for Group Id and Artifact Id. Then click Finish.
Go to Eclipse Help>Eclipse Marketplace> search cucumber. Install Cucumber Plugins. Refer below for snapshots.
- Cucumber Eclipse Plugin
- Natural 0.9
Once the project is created in Eclipse, the structure will look like below. By default the highlighted package will be created, so please go ahead and delete those packages.
Now we need to add dependency in our pom.xml.
Here are the list dependencies needed for our project. By default you will have JUnit dependency.
- Cucumber JVM: Java
- Cucumber JVM: JUnit
- Selenium Java
- WebDriverManager
- Apache Commons IO
- ExtentReports Cucumber7 Adapter
- ExtentReports
- Allure Cucumber 7 JVM
Once you added dependencies in your pom.xml please save it and right click on the project > select Maven>Update Project.
Once the project is updated, the Maven repository will look as shown below,
In order to make sure the project is configured with all the required Jars and Plugins, please go to the project and right click and select Run as>Maven install and click.
After clicking Maven install you should be able to see the Console output as Build success
Now we have completed setting up the project. Next, we can configure the project into a cucumber project. Right click on the project and go to Configure and select Convert to Cucumber Project as shown below
Step 2: Create Feature file and write feature.
We need to create feature files in the src/test/resources folder. For that we need to create a source folder under the project and name it as src/test/resources and click finish. Please refer screenshot below,
Under src/test/resources, create a folder and name it as features. Under features folder create a file and name it as login.feature in order to test the user login functionality. Features are defined in .feature files. Refer below snapshot
Most commonly used keywords in Gherkin : Feature, Scenario, Given, When, Then, And, But, Background, Scenario Outline, Examples etc.
This article uses Facebook login feature for testing. Based on Gherkin guidelines, let’s write a sample feature in login.feature.
Feature: Login
Background: The User opens login Page
Given User Launches Chrome Browser
When user opens facebook portal link "https://www.facebook.com/"
Then User should see the page title "Facebook - log in or sign up"
Scenario Outline: Validating the Login functionality
When User enters Username as "<username>" and Password as "<password>"
Then User clicks on Login button with expected status as "<status>"
Then User should see the Facebook Home page on successful login status "<status>"
Examples:
| username | password | status |
| | | Both Fail |
| Valid | | Missing Password |
| Valid | Valid | Pass |
Step 3: Create Page Objects
Here we have to implement page objects in separate classes for the UI page. We can call them wherever required in steps definitions. In future, if there are any changes in locators, we can just refer to the particular Page Object Model (POM) class and update at once.
By using this approach we can also reduce the code redundancy. Under src/test/java, create a package called ‘pageObjects’ and create a class called loginPage.java. Please refer snapshot below.
In LoginPage.java, define WebElements as variables using Annotation @findBy. And also create methods for actions performed on WebElements.
package pageObjects;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import stepDefinitions.BaseClass;
public class LoginPage extends BaseClass {
public LoginPage (WebDriver webDriver)
{
PageFactory.initElements(webDriver, this);
}
@FindBy(xpath="//input[@id='email']")
@CacheLookup
WebElement inputUsername;
@FindBy(xpath ="//input[@id='pass']")
@CacheLookup
WebElement inputPassword;
@FindBy(xpath="//button[@name='login']")
@CacheLookup
WebElement btnLogin;
@FindBy(xpath="//div/a[1][@aria-label='Facebook']")
@CacheLookup
WebElement divHomePageTitle;
@FindBy(xpath="//div[contains(text(),'The email')]")
@CacheLookup
WebElement divErrorMsgUN;
@FindBy(xpath="//div[contains(text(),'The password')]")
@CacheLookup
WebElement divErrorMsgPWD;
public void SetUserName(String uName) {
inputUsername.clear();
inputUsername.sendKeys(uName);
}
public void SetPassword(String pwd) {
inputPassword.clear();
inputPassword.sendKeys(pwd);
}
public void ClickBtnLogin() {
btnLogin.click();
}
public String getHomePgTitle() {
return divHomePageTitle.getAttribute("aria-label");
}
public String getErrMsgUN() {
return divErrorMsgUN.getText();
}
public String getErrMsgPWD() {
return divErrorMsgPWD.getText();
}
}
Step 4:Writing Step Definitions
Now under src/test/java, we need to create a package and name it as stepDefinitions. And under stepDefinitions create a class and name it as loginSteps. We also need to create BaseClass and Hooks under stepDefinitions. Here we used BaseClass so that we can avoid code duplication. We used Hooks to allow us to perform actions at various points in the cucumber test cycle. This is typically used for setup and teardown before and after each scenario. Please refer to the snapshot for better understanding.
Now how are we going to execute our scenario? After we added our feature files, we had to define and link steps. So here step definition comes into the picture. Step definition is the glue between the feature file written in Gherkin language. When Cucumber executes a Step in a Scenario, it will look for a matching Step Definition to execute. The yellow text shows in login.feature signifies steps within the defined feature that are not yet implemented. The easiest way to auto generate the step definitions stubs is to just run your feature file to get step definition suggestions in the console. Refer snapshot below,
Now our step definitions suggestion file will be populated in the console for the test cases in our feature file that do not have step definitions for a particular scenario. Copy the entire suggested step definitions and paste it in loginSteps. Then, remove the below highlighted yellow code in your loginSteps.
Now go to stepDefinitions package >loginSteps.java class and implement all the test steps as shown below:
package stepDefinitions;
import org.junit.Assert;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import pageObjects.LoginPage;
import utilities.Helper;
public class LoginSteps extends BaseClass {
@Given("User Launches Chrome Browser")
public void user_launches_chrome_browser() {
loginPage = new LoginPage(Helper.getDriver());
}
@When("user opens facebook portal link {string}")
public void user_opens_facebook_portal_link(String url) {
Helper.openPage(url);
}
@Then("User should see the page title {string}")
public void user_should_see_the_page_title(String string) {
String loginPgTitle = Helper.getTitle();
Assert.assertEquals(string, loginPgTitle);
}
@When("User enters Username as {string} and Password as {string}")
public void user_enters_username_as_and_password_as(String userName, String password) {
if(userName.equalsIgnoreCase("Valid") && password.equalsIgnoreCase("Valid"))
{
userName= Helper.GetUserName();
password= Helper.GetPassword();
}
loginPage.SetUserName(userName);
loginPage.SetPassword(password);
System.out.println("User Name: "+ userName);
System.out.println("Password: "+ password);
}
@Then("User clicks on Login button with expected status as {string}")
public void user_clicks_on_login_button(String expectedStatus) {
loginPage.ClickBtnLogin();
if (expectedStatus == "Both Fail")
{
String userNameValMessage = loginPage.getErrMsgUN();
String pwdValMessage= loginPage.getErrMsgUN();
Assert.assertEquals("Please enter your user name", userNameValMessage);
Assert.assertEquals("Please enter your password", pwdValMessage);
return;
}
else if (expectedStatus == "Missing Password")
{
String commonMsg =loginPage.getErrMsgPWD();
Assert.assertEquals("Invalid username and password Please try again",commonMsg);
return;
}
}
@Then("User should see the Facebook Home page on successful login status {string}")
public void user_should_see_the_facebook_home_page_on_successful_login_status(String status) throws InterruptedException {
if (status.equalsIgnoreCase("Pass"))
{
Assert.assertEquals(loginPage.getHomePgTitle(), "Facebook");
System.out.println("Login Passed");
Assert.assertEquals(Helper.getTitle(), "facebook");
}
}
}
Step 5: Writing Hooks/Base class
In the below example, we implemented Hooks for initial configurations of the project. Cucumber’s Before hook is mainly responsible for initializing the driver and opening a web browser which is a prerequisite for all scenarios. Cucumber’s After hook is responsible for capturing a snapshot of failure and close the browser. I have added the code to take the screenshot of the failed scenario in @After Hook.
package stepDefinitions;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
import utilities.Helper;
public class Hooks {
@Before
public static void setUp() {
Helper.setUpDriver();
}
@After
public static void tearDown(Scenario scenario) {
if(scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) Helper.getDriver()).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", scenario.getName());
}
Helper.tearDown();
}
}
We need to create Base class to maintain the page object classes. In our project we have only one page object class right now. In future if we have more page object classes we can manage them here and also any reusable methods that are applicable to all classes. Using Base class we can avoid code duplication.
package stepDefinitions;
import pageObjects.LoginPage;
public class BaseClass {
public LoginPage loginPage;
}
Step 6 : Create Helper Class
Helper class is responsible for initializing the web driver, web driver wait, defining the timeouts, creating a private constructor of the class to declare the web driver, so whenever we create an object of this class, a new web browser is invoked. This Helper Class contains methods that help in assisting our project.
Go to src/test/java> create package called Utilities. Under Utilities create a Class called Helper.java as shown below.
package utilities;
import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import io.github.bonigarcia.wdm.WebDriverManager;
public class Helper {
private static Helper Helper;
private static WebDriver driver ;
public final static int TIMEOUT = 2;
private Helper() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT));
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
driver.manage().window().maximize();
}
public static void openPage(String url) {
driver.get(url);
}
public static String getTitle() {
return driver.getTitle();
}
public static void NavBack() {
driver.navigate().back();
}
public static WebDriver getDriver() {
return driver;
}
public static void setUpDriver() {
if (Helper==null) {
Helper = new Helper();
}
}
public static void tearDown() {
if(driver!=null) {
driver.close();
driver.quit();
}
Helper = null;
}
public static String GetUserName() {
return "Facebook Username";
}
public static String GetPassword() {
return "Facebook password";
}
}
Step 7 : Executing the test using Test Runner
To run the tests from Junit we need to add a runner to the project. For this, create a class which will use JUnit annotation @RunWith(). @RunWith annotation will ask JUnit that test should run using cucumber.class, which is present in io.cucumber.junit.Cucumber dependency in POM.xml.
We are using @CucumberOptions that define the location of features, glue files which is called as step definitions, formatter plugins, and what reporting system to use, and other necessary options.
Lets see how to create the Test Runner:
Go to src/test/java>create package called testRunner. Under testRunner> create a class called testRunner.java as shown below,
package testRunner;
import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
@RunWith(Cucumber.class)
@CucumberOptions(features="src/test/resources/features",glue ="stepDefinitions"
,monochrome=true,
plugin= {"pretty","html:target/cucumber.html","io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm",
"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:",
})
public class testRunner {
}
Step 8: Create a property file for extent reports
Here we are going to use Extent Report. It is an open-source, powerful reporting library used for creating reports in beautiful and detailed way. This detailed report can show all the required information, including step-by-step analysis, screenshots, and better visuals. Lets see how to integrate the extent reports in our Cucumber-Junit project.
Go to src/test/java> create file called extent.properties as shown below.
extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html
extent.reporter.pdf.start=true
extent.reporter.pdf.out=PDFReport/ExtendPDF.pdf
#Screenshot
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
basefolder.name=ExtendReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
systeminfo.os=windows
systeminfo.Engineer= Gayathri
systeminfo.Project= CucumberProjectDemo
systeminfo.Browser= Google Chrome
Along with the extent html report we are also going to generate PDF report using the Extent Adapter.
Step 9 : Create folder for Driver/Extent Reports:-
Here we are using Chrome browser. It is impossible to run our test scripts on the Google Chrome browser without ChromeDriver. First download the latest version of ChromeDriver. The zip file is downloaded for the operating system, unzip it to retrieve the chromedriver.exe executable file. Copy this file to desired folder.
Here we are going to create two folders under our project. One is for Driver and another for Extent Reports.
Right click on the project CucumberProjectDemo>New>Folder. Please refer the snapshot below.
Create the folder and name it as Drivers and paste the chromedrive.exe file. In the same way, create another folder for Extent Report.
Step 10 : Executing the Tests
Now the project is ready for testing. Right Click on the testRunner.java and Click Run As > JUnit Test. Please refer snapshot below.
The output of Test Execution looks like below.
In order to see the reports we need to refresh our project as shown below,
Once the project is refreshed we will be able to see all the Extent reports, PDF, screenshots under our ExtentReports folder. Click on the folder to see all reports.
Our PDF reports should be like this.
Another important thing I need to mention here. We can generate Allure reports as well. We already applied dependency in our POM.xml and used the plugin in our Test Runner too. After running the test, refresh the project to see the Allure results folder as shown below,
In order to view the Allure reports, Right click on the Allure-results folder > properties> copy the location path as shown below.
Go to Command Prompt and type “allure serve” and give space and paste the path and press enter. Please see snapshot below
And the report will open in browser as shown below,
That's it !!!!!!!!!
I will show how to push the code to GitHub and run tests through Jenkins in my next article.
Enjoy Learning!!!