Automation QA Testing Course Content

BDD - Cucumber with Selenium

Test Driven Development (TDD)


Test Driver Development or TDD is the approach where automated test cases are created and 
executed before developing the actual application. 
Development involves creating the minimum amount of code to make the failed test cases pass.

Behaviour Driven Development (BDD):

Behaviour Driven Development evolved from TDD with focus to delivery of prioritised, 
verifiable business value by providing a common vocabulary that reduces the gap between the technical 
and business sides of the project.

BDD is a technique where your specifications or test cases are written in plain English. With this approach
non-technical team members find it easy to understand the flow and collaborate more in the process of Software
Development.

What is Cucumber Tool?

Cucumber is a testing tool that supports behavior-driven development (BDD). 

  • BDD bridges the gap between the stakeholders and technical team through a common platform. 
  • Gherkin is used as the language using which the test cases are written. It is a simple format and can also be read and modified by a non-technical user. 

In BDD, “Given-When-And-Then-But” is the proposed approach for writing test cases. Listed below is an example of BDD.

 Why Cucumber?
Cucumber is one of the most popular tools 
because of the reasons listed below
 1.Cucumber is open source and therefore free to use
 2.With Cucumber, you can write test scripts in multiple languages, such as java,Ruby,.Net,python, etc..
 3. It is also integrates with selenium, Ruby on Rails, Watir, and other web-based Testing tools
 4.Cucumber is one of the most widely used BDD tools. 
Tests written in cucumber involve the below three modules.

Feature:  I want to login into the site with valid data and Invalid data

   Background:

       Given I navigate to the Website

   Scenario: Login as a new sign-up user with valid data

           When I entered the user name

           And I entered the password

           And click on the sign-in button

           Then validate user successfully logged-in

   But log-in button is not present      

      Scenario: Login as a new sign-up user with invalid data

           When I entered an invalid user name

           And I entered the password

           And click on the sign-in button

           Then Error message should display

   But Re-login option be available

What are Gherkin Keywords

In the above example, there are many keywords, and every keyword has its own meaning and purpose.

Let’s explain each keyword with respect to the above example.

  • Feature Keyword: The feature file starts with the keyword FeatureUnder feature, you can mention the feature name which is to be tested, as seen below
  • I want to login into the site with valid data and Invalid data

Feature Files:

Feature file is a file with ‘.feature’ extension which describes the feature to test in an easily
readable descriptive language.
 The feature file usually contains a feature with multiple scenarios for testing that feature.
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). It is an essential part of Cucumber,
ou need to make sure that they carry the ‘.feature‘ file extension.

  • Scenario Keyword: There can be more than one scenario under one feature file, and each scenario starts with the keyword Scenariofollowed by the scenario name. In the below example, we can see there are two scenarios.
  • Scenario: Login as a sign-up user with valid data
    
    Scenario: Login as a sign-up user with invalid data

  • Given Keyword: Given keyword is normally used when we have to give some pre-condition in our test case as seen in the example below
  • Given User is on the home page of the site or
    
    Given User is on the  Log-in page of the site.

  • When Keyword: When the keyword is used to perform some action, as depicted in the example below
  • When the user enters an email or
    
    When the User clicks on the "Log in" button

  • And Keyword: There is no annotation for AND keyword. It is used to connect two statements and provides the logical AND condition between any two statements. AND can be used in combination with GIVENWHEN and THEN statements and used for positive assertion
  • When I entered my "username"
    
    And I entered password

  • Then Keyword: Then Keyword is used for the final outcome or for validation 
  • Then validate the user successfully logged in Or
    
    Then an Error message should display

  • But Keyword: But the keyword is used to represent negative assertions in addition to the previous statement.
  • Background Keyword: If there is any repetitive step that is going to be used in every test case. We just add the repetitive step under Keyword Background. Step keep under Background would be run before every test case.
  • Background: Given I navigate to the Website

Cucumber Best Practices for Effective Testing

 1. Write test scenarios as early as possible

It’s a good practice if we are ready to write test scenarios before creating test code. Developing scenarios early helps you define software behavior and understand possible problems in a better way. Once a scenario is ready and reviewed by team members who agree with it, we can start implementing the functionality. This will save time and effort and take away uncertainty later, during the development, or when your team will run acceptance tests.

2. Features and Scenarios

A feature is a product functionality, whereas a scenario is the specification of behavior.

Feature file:

  1. Feature file should be specific to the page, meaning if you have to cover the login scenario we should create a “login.feature”
  2. A single feature file can contain multiple scenarios
  3. Avoid mixing feature files
  4. Every feature should be ready to be executed alone

Scenario:

To describe the scenarios, Gherkin keywords are used: Given, When, Then, But and And During writing the scenario we have to make sure we should use keywords in an appropriate way. 

See below an example of a poorly written scenario

Scenario: As an existing user, I want to log in successfully

Given the user is on the Home page

When the user clicks on the login link on Home Page

And the user can see the login screen

And the user enters his username and password

And the user is able to click on the login button

Then the user is logged in successfully

And the successful login message is displayed

better way to write the same above scenario with fewer lines is as follows. It is best to avoid details that don’t contribute much.

Scenario: As an existing user, I want to log in successfully.

Given the user is on the Home page

When the user navigates to the Login page

And the user enters the username and password

Then the successful login message is displayed

3. Use Background

It is always the best practice to put steps that are repeated in every scenario into the Background. The background step is run before every scenario.

Ensure that you don’t put too many steps in there, as your scenarios may become hard to understand. Given below is an example.

Feature:  I want to login into the site with valid and invalid data

   Background:

       Given I navigate to the Website

   Scenario: Login as a new sign-up user with valid data

       When I entered a valid credential

           | email| validpassword |

           | qatubeupdate@yopmail.com | 12345         |

       When the user clicks on the sign-in button

       Then Validate the title after login

   Scenario: Login with invalid data by entering an invalid password

       When I entered an invalid credential

           | email | invalidpassword |

           | qatubeupdate@yopmail.com | 123456    |

       When the user clicks on the sign-in button

 Then Error message should display

           | errormessage |

           | Authentication failed |

4. Try to re-use step definitions

It’s best practice to reuse the step definitions that you frequently use in various scenarios. For example, Given: user navigate to the Website  can be one common step that we need in every scenario. So we can reuse this step.

Reusing the steps improves the maintainability, just in case there are any changes, you need to update in a single step.

Ensure the language/words are consistent if you want to re-use the step. In the below example, the steps are the same but the words (casing) are not consistent.

 Click and click are considered different because of the casing. 

Given (“Click on Sign In link on the Home Page”)

Given (“click on Sign In link on the Home Page”)

5. Use a Data Table

It’s recommended to use Data Table to store the data. You can give data as parameters within the step but once you have a large set of data. It is not feasible to relinquish all data as parameters. It’s better to use a data table once you have a large set of data.

A data table is used to inject data at the step level. The below example illustrates the steps for “I entered valid credential

Feature:  I want to login into the site with valid and invalid data

Background:

Given I navigate to the Website

@SmokeTest

Scenario: Login as a new sign-up user with valid data

When I entered valid credential

| email                    | validpassword |title|

| qatubeupdate1@yopmail.com | 12345         | Home   |

| qatubeupdate2@yopmail.com | 12345         | Home   |

| qatubeupdate3@yopmail.com | 12345         | Home   |

| qatubeupdate4@yopmail.com | 12345         | Home   |

When User click on sign-in button

Then Validate the title after login

6. Use a Scenario outline

Scenario outline injects the info at the scenario level rather than the step level. Scenario outline followed by the keyword.

Feature:  I want to login into the site with valid and invalid data

Scenario Outline: Login Validation

       Given I navigate to the Website

       When I enter "<email>" and "<validpassword>" to login Page

       And User click on sign-in button

       Then Validate the "<title>" after login

       Example:

           | email | validpassword |title|

           | qatubeupdate@yopmail.com | 12345 | Home   |

7. Use of Tags

Using tags could be the best practice once we want to run a bunch of test cases. There will be the case that we don’t want to execute all test cases in a single run or we would like to execute a group of the test cases. So best practice is to configure the test case by putting Tags on it. They are marked with @ followed by some text.

For example, a group of smoke test cases and sanity tests is created. You can put a tag over the scenario like @SmokeTest@SanityTest@RegressionTest, etc

Feature:  I want to login into the site with valid and invalid data

   Background:

       Given I navigate to the Website

   @SmokeTest

   Scenario: Login as a new sign-up user with valid data

       When I entered a valid credential

           | email                    | validpassword |

           | qatubeupdate@yopmail.com | 12345         |

       When a user clicks on sign-in button

       Then Validate the title after login

  @SanityTest

   Scenario: Login with invalid data by entering an invalid password

       When I entered an invalid credential

           | email | invalidpassword |

           | qatubeupdate@yopmail.com | 123456 |

       When the user clicks on the sign-in button

       Then Error message should display

           | errormessage  |

           | Authentication failed |

8. Map your scenario with the requirement

It’s a best practice to provide the required number (#Jira story id) after the scenario in the feature file. If any scenario fails then tracking the failed requirement from the execution report becomes easier (See below test case execution screenshot)


For example, in lines #6, and #14  of the code below, the required number is specified for better tracking.

Feature:  I want to login into the site with valid and invalid data

   Background:

       Given I navigate to the Website

   @SmokeTest

   Scenario: Login as a new sign-up user with valid data: QA-135, QA-156

       When I entered a valid credential

           | email | validpassword |

           | qatubeupdate@yopmail.com | 123451 |

       When the user clicks on the sign-in button

       Then Validate the title after login

  @SanityTest

   Scenario: Login with invalid data by entering the invalid password: QA-569, QA-281

       When I entered an invalid credential

           | email | invalidpassword |

           | qatubeupdate@yopmail.com | 123456 |

       When the user clicks on the sign-in button

       Then Error message should display

           | errormessage |

           | Authentication failed |

Test Result: The result of the above test can be seen below, where in the Smoke Test fails while the Sanity Test passes. Here, using the JIRA Story ID can be used to track the failed tests and debug


9. Avoid Conjunctive Steps

While writing the feature, it is best to avoid conjunctive steps. When you find a Cucumber step that contains two actions in conjunction with an “and”, you should break it into two steps keeping one action per step. This makes the steps more modular and increases reusability. This is not a general rule but a best practice.

10. Write in a declarative way, not Imperative

Scenarios should be written in a way the user would describe them. Beware of scenarios that only describe clicking links and entering data in form fields, or of steps that contain code. This is not a feature description. Declarative features are realistic, compendious, and contain highly maintainable steps.

Example of Declarative Scenario:

Scenario: Verify login

Given user navigate to the Website

When user enters credentials

Then the user clicks on the sign-in button

Then validate the title after login

Example of Imperative Scenario:

Scenario: Verify login

Given I navigate to the Website

When I enter username

When I enter password

When I check the Remember me check box

Then the user clicks on the sign-in button

Then Validate the title after login

11. Rules for writing Scenarios in Cucumber

Test scenarios should be written in a way that is easy to understand and clear for every team member. Below are some common recommendations 

  • Proper use of correct grammar and spelling 
  • Step definitions should not end with periods, commas, and other punctuation marks
  • Avoid jargon
  • Use correct punctuation

When dealing with behavior-driven automation, there are a ton of rules that you can use as guiding principles, the main core thing we have to keep in mind is one should properly use keywords GIVEN, WHEN, and THEN. Remember all the above points when we write test cases using the BDD framework.

What is Step Definition?

A Step Definition is a small piece of code with a pattern attached to it or in other words a 
Step Definition is a java method in a class with an annotation above it. An annotation followed 
by the pattern is used to link the Step Definition to all the matching Steps, 
and the code is what Cucumber will execute when it sees a Gherkin Step. Cucumber finds the Step
Definition file with the help of Glue code in Cucumber Options.
Gherkin code:


@Given("^User is on Linkedin Welcome page$")
public void user_is_on_Linkedin_Welcome_page() throws Throwable {
    // Write code here that turns the phrase above into concrete actions
assertTrue(driver.getTitle().contains("Welcome! | LinkedIn"));
}

@When("^user mouse over on profile image$")
public void user_mouse_over_on_profile_image() throws Throwable {
    // Write code here that turns the phrase above into concrete actions
act=new Actions(driver);
  
  act.moveToElement(page.progfileImage).perform();;
  
}

@When("^click on Signout link$")
public void click_on_Signout_link() throws Throwable {
    // Write code here that turns the phrase above into concrete actions
act.moveToElement(page.signout_link).click().build().perform();
   
}

@Then("^user is loggedout from linkedin$")
public void user_is_loggedout_from_linkedin() throws Throwable {
    // Write code here that turns the phrase above into concrete actions
 
assertTrue(driver.getTitle().contains("Signed Out | LinkedIn"));
}

-------------------------------------------------------------------

3. Test Runner file

Cucumber uses Junit framework to run

As Cucumber uses Junit we need to have a Test Runner class. This class will use the Junit 
annotation @RunWith(), 
which tells JUnit what is the test runner class. It more like a starting point for Junit to 
start executing your tests.
In the src folder create a class called TestRunner. 

package testRunner;
import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
@RunWith(Cucumber.class)
@CucumberOptions(features="D:\\eclipseworkspaceAug2018\\BddCucumberFrameworkJune172020\\src\\test\\java\\Feature",
glue={"step_def"},
plugin={"pretty","html:target/report.html", "json:json_output/cucumber.json", "junit:junit_xml/cucumber.xml"},
dryRun=false, //mapping between feature & stepdefinition is proper or not
strict=true, //it will check if any step is not defined in stepdefinition file
monochrome=false  //display the console output in a proper readable format
)
public class TestRunner {

}
Import Statements

First import statement ‘org.junit.runner.RunWith‘ imports @RunWith annotation from the Junit class. 
@RunWith annotation tells JUnit that tests should run using Cucumber class present in ‘Cucumber.api.junit‘ package.

Second import statement  io.cucumber.junit.CucumberOptions‘ imports the @CucumberOptions annotation. 
This annotation tells Cucumber a lot of things like where to look for feature files, what reporting system to use
 and some other things also

What is Cucumber Options ?

In layman language @CucumberOptions are like property file or settings for your test.
 Basically @CucumberOptions enables us to do all the things that we could have done 
 if we have used cucumber command line. This is very helpful and of utmost importance if we are using IDE such eclipse only to execute our project. You must have noticed that we set few options in the ‘TestRunner’ class


Run the Cucumber Test
Now we are all set to run the first Cucumber test. Right Click on TestRunner class and Click Run As  > JUnit Test. 


Dry Run

dryRun option can either set as true or false. If it is set as true, it means that Cucumber 
will only checks that every Step 
mentioned in the Feature File have corresponding code written in Step Definition file or not. 
So in case any of the function is missed in the Step Definition for any Step in Feature File, 
it will give us the message. 
For practice just add the code ‘dryRun = true‘ in TestRunner class:

TestRunner Class


package cucumberTest;

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

@RunWith(Cucumber.class)
@CucumberOptions(
features = "Feature"
glue={"stepDefinition"}
dryRun = true
)

public class TestRunner {

}
======================
Monochrome

This option can either set as true or false. If it is set as true, it means that the console
output for the Cucumber test are much more readable. And if it is set as false, then the console
output is not as readable as it should be. For practice just add the code ‘monochrome = true‘ in TestRunner class:
package cucumberTest;

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

@RunWith(Cucumber.class)
@CucumberOptions(
features = "Feature"
,glue={"stepDefinition"}
,monochrome = false
)

public class TestRunner {

}
================================================
Features

Features Options helps Cucumber to locate the Feature file in the project folder structure. You must have notices that we have been specifying the Feature Option in the TestRunner class since the first chapter. All we need to do is to specify the folder path and Cucumber will automatically find all the ‘.features‘ extension files in the folder. It can be specified like:

features = “Feature“

Or if the Feature file is in the deep folder structure 

features = “src/test/features“

======================================
Glue

It is almost the same think as Features Option but the only difference is that it helps Cucumber to locate the Step Definition file. Whenever Cucumber encounters a Step, it looks for a Step Definition inside all the files present in the folder mentioned in Glue Option. It can be specified like:

glue = “stepDefinition“

Or if the Step Definition file is in the deep folder structure 

glue = “src/test/stepDeinition“
================================================
plugin

plugin Option is used to specify different formatting options for the output reports. Various options that can be used as for-matters are:

 Pretty: Prints the Gherkin source with additional colours and stack traces for errors. Use below code:

plugin = {“pretty“}


HTML: This will generate a HTML report at the location mentioned in the for-matter itself. Use below code:

plugin = {“html:Folder_Name“}

 JSON: This report contains all the information from the gherkin source in JSON Format. This report is meant to be post-processed into another visual format by 3rd party tools such as Cucumber Jenkins. Use the below code:

plugin = {“json:Folder_Name/cucumber.json“}


JUnit: This report generates XML files just like Apache Ant’s JUnit report task. This XML format is understood by most Continuous Integration servers, who will use it to generate visual reports. use the below code:

plugin = { “junit:Folder_Name/cucumber.xml“}
==============================================
tags: we can run the cucumber test using tags. under cucumber options specify the tags={"@tagname"}
tagname is mentioned in feature file above Feature or scenario name

============================================

Scenario Outline – This is used to run the same scenario for 2 or more different set of test data.

 E.g. In our scenario, if you want to register another user you can data drive the same scenario twice.

Examples – All scenario outlines have to be followed with the Examples section. 
This contains the data that has to be passed on to the scenario.

Examples:
          | username  | password  |
          | testuser_1 | Test@153 |
          | testuser_2 | Test@153 |
  
Note: The table must have a header row corresponding to the variables in the Scenario Outline steps.

=======================================

Data Tables in Cucumber

DataTable is a simple data structure that allows the use and transformation of Gherkin data tables in Cucumber.

In this example, we will pass the test data using data table and handle it with using raw() method.

Scenario: Successful Login with Valid Credentials
Given User is on Home Page
When User Navigate to LogIn Page
And User enters Credentials to LogIn
    | testuser_1 | Test@153 |
Then Message displayed Login Successfully

StepDefinitionFile:
The implementation of the above step will belike this:
@When("^User enters Credentials to LogIn$")
public void user_enters_testuser__and_Test(DataTable usercredentials) throws Throwable {

//Write the code to handle Data Table
List<List<String>> data = usercredentials.asLists();

//This is to get the first data of the set (First Row + First Column)
driver.findElement(By.id("log")).sendKeys(data.get(0).get(0)); 

//This is to get the first data of the set (First Row + Second Column)
    driver.findElement(By.id("pwd")).sendKeys(data.get(0).get(1));

    driver.findElement(By.id("login")).click();
}

Data Tables in Cucumber are quite interesting and can be used in many ways. DataTables are also used to handle large amount of data. They are quite powerful but not the most intuitive as you either need to deal with a list of maps or a map of lists. Most of the people gets confused with Data tables & Scenario outline, but these two works completely differently.

===========================================

 Difference between Scenario Outline & Data Table

Scenario Outline:

This uses Example keyword to define the test data for the Scenario
This works for the whole test
Cucumber automatically run the complete test the number of times equal to the number of data in the Test Set
Test Data:

No keyword is used to define the test data
This works only for the single step, below which it is defined
A separate code is need to understand the test data and then it can be run single or multiple times but again just for the single step,
 not for the complete test


================================================

Maps in Data Tables:

Maps in Data Tables can be used if different ways. Headers can also be defined for the data tables. A same step can be executed multiple times with different set of test data using Maps.
Maps in Data Tables with Header

In the previous chapter of Data Tables in Cucumber,  we pass Username & Password without Header, due to which the test was not much readable. What if there will be many columns. The basic funda of BDD test is to make the Test in Business readable format, so that business users can understand it easily. Setting Header in Test data is not a difficult task in Cucumber. take a look at a below Scenario.
Feature file scenario
Scenario: Successful Login with Valid Credentials
Given User is on Home Page
When User Navigate to LogIn Page
And User enters Credentials to LogIn
| Username   | Password |
    | testuser_1 | Test@153 |
Then Message displayed Login Successfully

The implementation of the above step will be like this:
@When("^User enters Credentials to LogIn$")
public void user_enters_testuser_and_Test(DataTable usercredentials) throws Throwable {

//Write the code to handle Data Table
List<Map<String,String>> data = usercredentials.asMaps(String.class,String.class);
driver.findElement(By.id("log")).sendKeys(data.get(0).get("Username")); 
    driver.findElement(By.id("pwd")).sendKeys(data.get(0).get("Password"));
    driver.findElement(By.id("login")).click();
           }
================================================

How To Work With Cucumber Hooks

Why Cucumber Hooks?

Hooks are blocks of code that run before or after each scenario in the Cucumber execution cycle. This allows us to manage the code workflow better and helps to reduce code redundancy. 

Hooks can be defined anywhere in the project or step definition layers using the methods @Before and @After. They are typically used for setup and tear-down of the environment before and after each scenario.

@Before - Before hooks run before the first step of each scenario. This is commonly used for prerequisite steps that need to be performed before the actual test scenario. For example, this can be as follows.

1. Initialize a web driver: This is the most common use case. We need to initialize the driver once before launching the test.

  // Define driver

  WebDriver driver;

2. Establish DB connections: Application may require access to test data at the start of the test. A couple of examples could be -

  > read data through any external sources like DB.

3. To set up test data: Application may require to access test data at the start of the test. A couple of examples could be -

  > read date from the property file

  > read data through any external sources like DB, XL, JSON, etc.

4. To set browser cookies: Certain times, the application requires to set some cookies to achieve the functional goal.

5. Navigate to default page: Whenever a test is launched, it may need to navigate to the default application URL.

 driver.get("<<APP URL >> /admin");

Annotated method style:

@Before

public void setup() {

// Do something before each scenario

}

@After - After hooks run after the last step of each scenario, even when steps are failed, undefined, pending, or skipped. This is commonly used for steps that need to be performed after the actual scenario gets executed. For example, this can be as follows.

 

1. Quit the web driver: This is most commonly used. After each test, we are supposed to kill the browser in order to make tests independent.

//Quit driver

driver.quit();

2. To close DB connections: If we have established the DB connection at the beginning of the test, then it is advisable to terminate at the end of the tests.

3. To clear the test data/browser cookies: As @After hook will execute at the end of each test, we have clean up activity here.

4. Sign out from the application: Signing out from the application is really essential in order to make tests independent.

5. Take screenshots for fail/pass scenarios: In order to make sure the test runs correctly, We always need to take a screenshot in case of any failure.

Annotated method style:

@After

public void teardown(Scenario scenario) {

// Do something after each scenario

}

Run Hooks with Single Scenario

  • Hooks execute before and after scenario
  • package com.blog.common;
    import org.openqa.selenium.WebDriver;
    
    public class BaseClass {
    	public String baseURL = "http://automationpractice.com";
    	public WebDriver driver;
    }
  •  Login Feature File
  • Feature: Login Feature
      As an authenticated
      I want to login to application
      So that I can access application feature
    
      Scenario: Check valid login
        Given user is on Login Page
        When user has provided valid credentials
        Then user should be able to login
  • StepDefinition for Login featureFile
  • package com.blog.stepdefs;
    
    import org.openqa.selenium.By;
    import org.openqa.selenium.Keys;
    import org.testng.Assert;
    import com.blog.common.BaseClass;
    import cucumber.api.java.en.And;
    import cucumber.api.java.en.Given;
    import cucumber.api.java.en.Then;
    import cucumber.api.java.en.When;
    
    public class LoginSteps extends BaseClass {
    
    	private BaseClass base;
    
    	public LoginSteps(BaseClass base) {
    		this.base = base;
    	}
    
    	@Given("^user is on Login Page$")
    	public void user_is_on_Login_Page() throws Throwable {
    base.driver.get(base.baseURL+"/index.php?controller=authentication&back=my-account");
    	}
    
    	@When("^user has provided valid credentials$")
    	public void user_has_provided_valid_credentials() throws Throwable {
    	base.driver.findElement(By.id("email")).sendKeys("blog.cucumber@gmail.com");
    		base.driver.findElement(By.id("passwd")).sendKeys("Cucumber@blog");
    		base.driver.findElement(By.id("SubmitLogin")).click();
    	}
    
    	@Then("^user should be able to login$")
    	public void user_should_be_able_to_login() throws Throwable {
    		String exp_message = "Welcome to your account. Here you can manage all of your personal information and orders.";
    		String actual = base.driver.findElement(By.cssSelector(".info-account")).getText();
    		Assert.assertEquals(exp_message, actual);
    	}
  • ---------------------------------------------------------------
  • Hooks Implementation:
  • package com.blog.stepdefs;
    
    import java.util.concurrent.TimeUnit;
    import org.openqa.selenium.By;
    import org.openqa.selenium.chrome.ChromeDriver;
    import com.blog.common.BaseClass;
    import cucumber.api.java.After;
    import cucumber.api.java.Before;
    
    public class Hook extends BaseClass{
    	
    	private BaseClass base;
    	
    	public Hook(BaseClass base) {
    		this.base = base;
    	}
    
    	@Before
    	public void initDriver() {
    		System.out.println("Open browser");
    		System.setProperty("webdriver.chrome.driver", "lib/chromedriver 3");
    		base.driver = new ChromeDriver();
    		base.driver.manage().window().maximize();
    		base.driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
    	}
    
    	@After
    	public void teardown() {
    		System.out.println("Close browser");
    		base.driver.quit();
    	}
    }
  • ---------------------------------------------
    • It is not recommended to have too many print statements in the Automation code as it slows down the execution. Here it has been added just for the explanation purpose.
    • It’s always recommended to place the chrome driver in the project folder itself. For that, create a lib folder in the project and place the chrome driver file in the folder. The project structure will look like below.

    • Execution

      To execute the above code, right-click login.feature file → Run As → Cucumber Feature. On executing the login.feature file it will show below output in the console. Here I have executed with Chrome.


    • Run Hooks with Multiple Scenarios

      • As stated earlier, Scenario Hooks execute before and after every scenario. In the below example, both the Before and After hooks are executed two times for two scenarios.

      Execution Order:


Login.Feature:
Feature: Login Feature
  As an authenticated
  I want to login to application
  So that I can access application feature

  Scenario: Check valid login
    Given user is on Login Page
    When user has provided valid credentials
    Then user should be able to login

  Scenario: SearchProduct
    Given user is on Login Page
    When user has provided valid credentials
    Then user search for the product
------------------------------------------------------------------------------

Output:


How to set the Priority of Cucumber Hooks?

  • Priority in Cucumber is almost the same as a priority in TestNG
  • Cucumber executes Hooks in a certain order but there is a way to change the order of the execution according to the need for the test.
  • @Before(order = int): This runs in increment order, means value 0 would run first and 1 would be after 0
  • @After(order = int): This runs in decrements order, which means the opposite of @Before. Value 1 would run first and 0 would be after 1.
package com.blog.stepdefs;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import com.blog.common.BaseClass;
import cucumber.api.java.After;
import cucumber.api.java.Before;

public class Hook extends BaseClass{
	
	private BaseClass base;
	
	public Hook(BaseClass base) {
		this.base = base;
	}

	@Before(order = 0)
	public void initDriver() {
		System.out.println("Open browser");
		System.setProperty("webdriver.chrome.driver", "lib/chromedriver 3");
		base.driver = new ChromeDriver();
		base.driver.manage().window().maximize();
		base.driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
	}
	
	@Before(order = 1)
	public void maximizeWindow() {
		System.out.println("Maximize window");
		base.driver.manage().window().maximize();
	}
	
	@After(order = 1)
	public void signOut() {
		System.out.println("Sign out from app");
		base.driver.findElement(By.linkText("Sign out")).click();
	}

	@After(order = 0)
	public void teardown() {
		System.out.println("Close browser");
		base.driver.quit();
	}

}

The execution order of Hooks


  • The above diagram explains the order of execution.
  • Execution order will be Before Hook 0 -> Before Hook 1 -> Scenario ->  After Hook 1 -> After Hook 0.
  • Before the first step of each scenario, Before hooks will be run. Execution order is the same order of which they are registered. After the last step of each scenario, After hooks will be run. It doesn't matter even when there are failing, undefined, pending or skipped steps.
  • The above diagram explains the order of execution.
  • Execution order will be Before Hook 0 -> Before Hook 1 -> Scenario ->  After Hook 1 -> After Hook 0.
  • Before the first step of each scenario, Before hooks will be run. Execution order is the same order of which they are registered. After the last step of each scenario, After hooks will be run. It doesn't matter even when there are failing, undefined, pending or skipped steps.
  • Tagged Hooks

    In the above topic explained how hooks are executed before or after each scenario. Tagged hooks are almost similar but the only difference is that they are executed before and after the specified tag. 

    If we have different prerequisites for different scenarios then we need to have different hooks for different scenarios. Let’s say we have two different tags smoke and regression and we want to perform different operations based on the tag then such things can be achieved through tagged hooks.

    In the below feature file, two scenarios are tagged with @Smoke and one tagged with @Regression 

    Login.Feature

    Feature: Login Feature
      As an authenticated
      I want to login to application
      So that I can access application feature
    
     @Smoke
      Scenario: Check valid login
        Given user is on Login Page
        When user has provided valid credentials
        Then user should be able to login
    
    @Smoke
    Scenario: SearchProduct
        Given user is on Login Page
        When user has provided valid credentials
        Then user search for the product
        
     @Regression
    Scenario: Check invalid login
        Given user is on Login Page
        When user has provided invalid credentials
        Then user should not be able to login
Sometimes there could be common pre or post steps. In such cases, we can combine them in hooks. If two methods are tagged with the same tag, it will be executed alphabetically. 
Hooks:
package com.blog.stepdefs;

import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import com.blog.common.BaseClass;
import cucumber.api.java.After;
import cucumber.api.java.Before;

public class Hook extends BaseClass{
	
	private BaseClass base;
	
	public Hook(BaseClass base) {
		this.base = base;
	}

	@Before
	public void initDriver() {
		System.out.println("Open browser");
		System.setProperty("webdriver.chrome.driver", "lib/chromedriver 3");
		base.driver = new ChromeDriver();
		base.driver.manage().window().maximize();
		base.driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
	}

	@After("@Smoke")
	public void signOut() {
		System.out.println("Sign out from app");
		base.driver.findElement(By.linkText("Sign out")).click();
	}

	@After("@Regression,@Smoke")
	public void quitBrowser() {
		System.out.println("Close browser");
		base.driver.quit();
	}
}
================================================================================
TestRunner.java
--------
package com.blog.runner;

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

@RunWith(Cucumber.class)
@CucumberOptions(features= "src/test/java/com/blog/features",
				glue= {"com.blog.stepdefs"},
				tags = {"@Smoke,@Regression"})
public class TestRunner {

}
--------
OUTPU:


===========================================

Setting up Maven BDD Cucumber selenium framework:

Step1:

1)Instal JDK 8 or JDK 11

2)download Eclipse/Intellij

3)set below paths in environment variables

  i)java home

  ii)Maven home

4)Install Cucumber JVM Eclipse plugin from help market place in eclipse orintellij cucumber plugin

Install Cucumber plugin:
  • In Eclipse, go to Help → Install new software
  • On the Available Software popup, enter the URL “ http://cucumber.github.com/cucumber-eclipse/update-site ” in the Work with field.


  • You will see “Cucumber Eclipse Plugin” displayed in the filter; select the checkbox and click Next, and you will navigate to the Install Details popup. Click Next to proceed further.
  • Accept the license in the Review License pop-up and click Finish.

Why Maven?

Maven is a automation build tool and is widely used for Java projects. It is mainly used in managing dependencies through pom.xml. Suppose you want to upgrade the JAR files and in your project you are using version 1.25 for Cucumber-Java dependency. You need to upgrade to the latest version. With the use of Maven, it’s easy to upgrade the version.

Set up your Maven project

Step 1: To create a Maven Project in Eclipse, click on New → Project → In the wizard, select Maven Project.

Step 2: On the new Maven Project pop-up, select the checkbox to create your project at the default location OR you can also browse and set a new location of your choice. Click on Next to proceed.

Step 3: On the next screen, by default the Group ID and Artifact ID org.apache.maven.archetypes maven-archetypes-quickstart 1.1 is selected. Click on Next to proceed.

Step 4: In the next screen, you will have to mention a Group ID and Artifact ID of your own choice; this is the name of your Maven project. Once you click the Finish button, a Maven project will be created in Eclipse

The structure of the project created in Eclipse will be similar to the following image.

As you can see, there is a pom.xml file created in your Maven project. This file consists of the Group ID and Artifact ID you entered, and by default it consists of dependency for JUnit. Refer to the screenshot below.

Step 5: Now, in order to build a Selenium-Cucumber framework for us to work with, we need to add dependency for Selenium and Cucumber in pom.xml, which is somewhat similar to adding JAR files. We will be needing dependencies of the following: 

Selenium-java
Cobertura
Cucumber-jvm-deps
Cucumber-reporting
Gherkin
JUnit
Mockito-all-1.10.19
Cucumber-core
Cucumber-java
Cucumber-junit

Note: Make sure the versions on Cucumber-java, Cucumber -junit and Cucumber-core are the same, i.e., if you are using Cucumber-java-1.2.5 make sure the versions of the other two dependencies are the same.

Step 6: To add dependencies for the above, you should refer to https://mvnrepository.com/. After adding dependencies for Cucumber and Selenium, the pom.xml file will look like this:

================================================================

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Cucumber_Selenium</groupId>
<artifactId>Cucumber_Selenium</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Cucumber_Selenium</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.16.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-jvm-deps</artifactId>
<version>1.0.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>1.2.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.vimalselvam</groupId>
<artifactId>cucumber-extentsreport</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>3.1.2</version>
</dependency>
</dependencies> 
</project>


Step 7: Make sure to update the project after adding dependencies to pom.xml; you can do that by right clicking Project → Maven → Update Project. Once you update the project, you will see that many JAR files are added to the Maven Dependencies folder in your project.






---------------------------------------------------------------------------------------------------------------
Step 3: Create a folder structure
    Project Name
            |
    Features[Folder]
            |
    Page Objects[pckage]
            |
    Utilities[package]
            |
    stepdefinitions[package]
            |
        runner[package]
            |
        Target
            |
    Reports

1)Create a Login.feature file under Feature folder
Feature: Login feature

  Scenario: Sucessful login with valid credentials
    Given I want to open chrome browser
    And User opens URL "https://admin-demo.nopcommerce.com/login"
    When User enters email as "admin@yourstore.com" and password as "admin"
    And click on login button
    Then page title should be "Dashboard / nopCommerce administration"
    When user clicks on logout link
    Then page title should be "Your store. Login"
    And close the browser

Scenario Outline: Login Data Drievn Test
    Given I want to open chrome browser
    And User opens URL "https://admin-demo.nopcommerce.com/login"
    When User enters email as "<email>" and password as "<pwd>"
    And click on login button
    Then page title should be "Dashboard / nopCommerce administration"
    When user clicks on logout link
    Then page title should be "Your store. Login"
    And close the browser

    Examples: 
      | email                  | pwd   |
      | admin@yourstore.com    | admin |
      | admin123@yourstore.com | admin |

2)Create loginpage.java file under pageObjects package
package pageObjects;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class LoginPage {

public WebDriver driver;

public LoginPage(WebDriver driver) {
this.driver=driver;
PageFactory.initElements(driver, this);
}

@FindBy(id="Email")
private WebElement emailEditbox;

@FindBy(name="Password")
private WebElement passwordEditbox;

@FindBy(css="button.button-1.login-button")
private WebElement loginBtn;

@FindBy(linkText="Logout")
private WebElement logoutLink;

public void setEmail(String email) {
emailEditbox.clear();
emailEditbox.sendKeys(email);
}

public void setPassword(String pwd) {
passwordEditbox.clear();
passwordEditbox.sendKeys(pwd);
}

public void clickLoginBtn() {
loginBtn.click();
}

public void clickLogOutLink() {
logoutLink.click();
}

public boolean isElementPresent(WebDriver driver, By by) {
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
try {
driver.findElement(by);
return true;
}catch(Exception e) {
return false;
}
}

}

==================================================

3. Create Stepdefinition file under step_def package
-----------------------------------------------------------
package step_def;

import java.time.Duration;

import org.junit.Assert;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

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 LoginStepDefinition {
private WebDriver driver;
LoginPage lpg;
private WebDriverWait wait;

@Given("I want to open chrome browser")
public void i_want_to_open_chrome_browser() {
//launch the browser
WebDriverManager.chromedriver().setup();
driver=new ChromeDriver();
//maximize the browser
driver.manage().window().maximize();
//add implicitwait
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
wait=new WebDriverWait(driver,Duration.ofSeconds(30));
lpg=new LoginPage();
}

@Given("User opens URL {string}")
public void user_opens_URL(String url) {
driver.get(url);
}

@When("User enters email as {string} and password as {string}")
public void user_enters_email_as_and_password_as(String email,String pwd) {
lpg.setEmail(email);
lpg.setPassword(pwd);
}

@When("click on login button")
public void click_on_login_button() {
lpg.clickLoginBtn();
}

@Then("page title should be {string}")
public void page_title_should_be(String title) {
if(driver.getPageSource().contains("Login was unsuccessful")) {
Assert.assertTrue(driver.getTitle().contains("Your store. Login"));
//Assert.assertTrue(false);
}else {
Assert.assertEquals(title, driver.getTitle());
}
}

@When("user clicks on logout link")
public void user_clicks_on_logout_link() {
lpg.clickLogOutLink();
}

@Then("close the browser")
public void close_the_browser() {
if(driver!=null) {
driver.quit();
}
}

}
==========================================================
4. Create Runner java file under runner package
package runner;
import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)
@CucumberOptions(features="D:\\eclipseworkspaceAug2018\\BddCucumberFrameworkApr72022\\src\\test\\java\\Feature",
glue="step_def",
plugin={"pretty","html:target/cucumber-report.html", "json:json_output/cucumber.json", "junit:junit_xml/cucumber.xml"},
dryRun=false, //mapping between feature & stepdefinition is proper or not
monochrome=true //console output will show in readable format
)
public class TestRunner {

}
=========================================================================
Creating 2nd feature file:
Featue: Customers
Scenario: add a new customer
Given User launch chrome browser
When User opens URL "https://admin-demo.nopcommerce.com/login"
When User enters email as "admin@yourstore.com" and password as "admin"
And click on login button
Then User can view Dashboard
When User click on Customers menu
And click on customers menu Item
And click on Add new button
Then User can view add new customer page
When User enter customer info
And click on save button
Then User can view confirmation message "The new customer has been added successfully"
And close browser

Scenario: search customer by EmailId
Given User launch chrome browser
When User opens URL "https://admin-demo.nopcommerce.com/login"
When User enters email as "admin@yourstore.com" and password as "admin"
And click on login button
Then User can view Dashboard
When User click on Customers menu
And click on Customers menu Item
And enter customer Email
When Click on search button
Then User should found Email in the search table
And close browser

Scenario: Search Customer by Name
Given User launch chrome browser
When User opens URL "https://admin-demo.nopcommerce.com/login"
When User enters email as "admin@yourstore.com" and password as "admin"
And click on login button
Then User can view Dashboard
When User click on Customers menu
And click on Customers menu Item
And enter customer FirstName
And enter customer LastName
When Click on search button
Then User should found Name in the search table
And close browser


---------------------------------------------------------------------------------------------------------------
Step2)
Create CustomerPage.java file Under pageObjects package



=================================================================
step3)create stepdefinition file under step_def package



Common Issues while Running Maven Project:

1)
Given I want to open chrome browser                                        # step_def.WebUiCommonSteps.i_want_to_open_chrome_browser()
      java.lang.NoSuchMethodError: com.google.common.collect.ImmutableMap.of(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Lcom/google/common/collect/ImmutableMap;
at org.openqa.selenium.chrome.AddHasCasting.getAdditionalCommands(AddHasCasting.java:38)
at org.openqa.selenium.chrome.ChromeDriver$ChromeDriverCommandExecutor.getExtraCommands(ChromeDriver.java:123)
at org.openqa.selenium.chrome.ChromeDriver$ChromeDriverCommandExecutor.<init>(ChromeDriver.java:118)
at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:106)
at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:93)
at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:48)
at step_def.WebUiCommonSteps.i_want_to_open_chrome_browser(WebUiCommonSteps.java:21)
at ✽.I want to open chrome browser(file:///D:/eclipseworkspaceAug2018/BDDCucumberWithSeleniumMay222022/src/test/java/Features/Login.feature:15)


Solution:
you can get the solution for above problem in below link

https://stackoverflow.com/questions/71095560/java-lang-nosuchmethoderror-com-google-common-collect-immutablemap-error-when

Create HTML reports in Cucumber

Imagine that you have to share the test reports with your client and senior management; in that case you will need a shareable HTML report which you can share after executing your tests.

You can achieve this by following some very simple steps.

Create an HTML report by adding a plugin to testrunner.java class

Step 1: In your testrunner.java class, add a plugin inside @CucumberOptions to format your test results into the HTML format.

plugin = { "pretty", "html:target/htmlreports" }

In order to set the path for the reports, we have to give a path in the project. To make this easier, the path is target/htmlreports.

Step 2: Now save the testrunner.java class and execute it. On execution, you will see that the folder htmlreports is created inside the target folder.

Step 3: Access the folder and look for the index.html file; that is the file which contains the test results in HTML format.



Step 4: Open the index.html to view the report. The report created would be similar to the image below.

Create HTML report by using extent-reports

We have already seen how to create an HTML test report, but with the help of extent reports we can create more well-organized and detailed reports.

Step 1: To implement extent report, we need to add two dependencies to the pom.xml and update the project after adding the dependency.

Cucumber-extentsreport
extentreports

The dependencies for the above would be like this:

<dependency>
    <groupId>com.vimalselvam</groupId>
    <artifactId>cucumber-extentsreport</artifactId>
    <version>3.0.2</version>
   </dependency>

    <dependency>
    <groupId>com.aventstack</groupId>
    <artifactId>extentreports</artifactId>
    <version>3.1.2</version>
   </dependency>

Step 2: Add a new folder to the project. Eg. “config” by right clicking the project folder → New → Folder → Config. Now we have to add an XML file to this folder. This XML file states the theme of the report, title, etc. The report.xml file would be like this:

<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
  <configuration>
    <!-- report theme --> <!-- standard, dark -->
    <theme>standard</theme>
  
    <!-- document encoding -->  <!-- defaults to UTF-8 -->
    <encoding>UTF-8</encoding>
    
    <!-- protocol for script and stylesheets -->   <!-- defaults to https -->
    <protocol>https</protocol>
    
    <!-- title of the document -->
    <documentTitle>Selenium Cucumber Framework</documentTitle>
    
    <!-- report name - displayed at top-nav -->
    <reportName>Functional Testing report</reportName>
    
    <!-- global date format override -->  <!-- defaults to yyyy-MM-dd -->
    <dateFormat>yyyy-MM-dd</dateFormat>
    
    <!-- global time format override -->   <!-- defaults to HH:mm:ss -->
    <timeFormat>HH:mm:ss</timeFormat>
    
    <!-- custom javascript -->
    <scripts>
      <![CDATA[
        $(document).ready(function() {
        
        });
      ]]>
    </scripts>
    
    <!-- custom styles -->
    <styles>
      <![CDATA[
        
      ]]>
    </styles>
  </configuration>
</extentreports>

Step 3: Now we are almost ready with the setup required for the report, but in order to fetch the report for every test, we need to add a plugin in testrunner.java and add an @AfterClass. In the plugin, we will mention the Extent formatter and the location where we want the report to be saved, and in the after class, we will write a function to load the report.xml. The final testrunner.java class would be like this:
package Runner;

import java.io.File;

import org.junit.AfterClass;
import org.junit.runner.RunWith;

import com.cucumber.listener.ExtentCucumberFormatter;
import com.cucumber.listener.Reporter;

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

@RunWith(Cucumber.class)
@CucumberOptions(
        features ="src/test/java/features"
        ,glue= "seleniumgluecode",
        plugin = { "com.cucumber.listener.ExtentCucumberFormatter:target/cucumber-reports/report.html"}, 
        monochrome = true
                )

public class testrunner {
    @AfterClass
    public static void writeExtentReport() {
        Reporter.loadXMLConfig(new File("config/report.xml"));
    
    }
}

Step 4: On executing the tests, a new folder will be created at the path mentioned in the plugin. Open the folder and open the report.html.

The report created will have a heading, graph of the test results, detailed results for all features executed, and you can also filter the test results with the status Pass/Fail by clicking the Status menu.

Command line execution

Executing your Cucumber tests from Eclipse is very easy, but you can also execute them through the command line. The steps to execute the tests through the command line are as follows:

  1. Open the terminal in your system and navigate to your project directory.
  2. Since we have already added a Maven dependency through pom.xml, we can execute the test using the simple command mvn test
  3. In case you have a large number of feature files added to your project, and you only want to execute a smoketest.feature file, you can use the command mvn test -Dcucumber.options=”src/test/java/Features/smoketest.feature.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.