Enroll in Selenium Training

This chapter is very much related and dependent to sharing Test Context between the Cucumber Steps. We know in Cucumber tests there are situations where we need to carry a data or a state from one step to another. This data or state can be anything but in broader term, we can distinguish these into two levels:

  • Test Context
  • Scenario Context

Scenario Context in Cucumber

Now the question arises that what is Test Context and Scenario Context and what is the difference between two. Just to keep things simple, we can say that the TestContext is the parent class and the medium to share the information between the different steps in a test. It can have many class objects in it. If you go back to our previous tutorial of Test Context, you will realize that it already has PageObjectManager and WebDriverManager object in it.

Other hand Scenario Context is a class that holds the test data information specifically. It actually uses the Test Context to travel the information between various steps. Within this ScenarioContext class, you can create any number of fields to store any form of data. It stores the information in the key-value pair and again, value can be of any type. It can store String, Boolean, Integer or maybe a Class. Also, the important point here is that the information which we store in the Scenario Context is generated run time. Means during the run if you wish to store some information, you will use Scenario Context. Structure of the TestContext class will be like this:

public class TestContext {
	WebDriverManager webDriverManager;
	PageObjectManager pageObjectManager;
	ScenarioContext scenarioContext;
}

Share data between steps in Cucumber using Scenario Context

Let's get back to our Test Scenario and put a validation. So far we have just written an End 2 End test Case but we have not validated anything in the test. Let's say we want to validate the Name of the Product at the end of the test. In that case, we need to store the name of the product in Scenario Context object while adding product in the bag. And retrieve the product from the ScenarioContext and validate the name on the final confirmation page, after the order placement is done.

Step 1: Design a Scenario Context class

  1. Create a New Enum and name it as Context by right click on the enums package and select New >> Enum. Just add a Product_Name in the Context Enum as of now.

Context.java

package enums;

public enum Context {
	PRODUCT_NAME;
}
  1. Create a New Class and name it as ScenarioContext by right click on the cucumber package and select New >> Class.
package cucumber;

import java.util.HashMap;
import java.util.Map;
import enums.Context;

public class ScenarioContext {
	 
	private  Map<String, Object> scenarioContext;

	    public ScenarioContext(){
	        scenarioContext = new HashMap<>();
	    }

	    public void setContext(Context key, Object value) {
	        scenarioContext.put(key.toString(), value);
	    }

	    public Object getContext(Context key){
	        return scenarioContext.get(key.toString());
	    }

	    public Boolean isContains(Context key){
	        return scenarioContext.containsKey(key.toString());
	    }

}

Explanation

  • scenarioContext : This is a HasMap object which stores the information in the Key-Value pair. Key type is String and Value can be of any Object Type.
  • setContext(): This method takes two parameters,  key as String and value as object. Key is nothing but a Context enum.
  • getContext(): This method takes the key as a parameter and returned the object which matches the key.
  • isContains(): This method performs a check on the complete Map that if it contains the key or not.
  1. Include ScenarioContext in TextContext, so that it can be shared across all the Cucumber Steps using Pico-Container library. Also, to make sure to add a getter method as getScenarioContext() to get the scenarioContext object.

TestContext.java

package cucumber;

import managers.PageObjectManager;
import managers.WebDriverManager;

public class TestContext {
	private WebDriverManager webDriverManager;
	private PageObjectManager pageObjectManager;
	public ScenarioContext scenarioContext;
	
	public TestContext(){
		webDriverManager = new WebDriverManager();
		pageObjectManager = new PageObjectManager(webDriverManager.getDriver());
		scenarioContext = new ScenarioContext();
	}
	
	public WebDriverManager getWebDriverManager() {
		return webDriverManager;
	}
	
	public PageObjectManager getPageObjectManager() {
		return pageObjectManager;
	}
	
	public ScenarioContext getScenarioContext() {
		return scenarioContext;
	}

}

Step 2: Save test information/data/state in the Scenario Context

To use the value of the product name later in the test for the validation, it is required to save the Name as first. In the below step of the Test Scenario, we are adding product in the bag. It makes sense to save the name at this time to ScenarioContext object.

  1. Add a new method getProductName() in the ProductListingPage class which will return the Name of the Product. This method will take the product position as a parameter and return its name only.

ProductListingPage.java

package pageObjects;

import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindAll;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
import org.openqa.selenium.support.PageFactory;
import selenium.Wait;

public class ProductListingPage {
	WebDriver driver;
	
	public ProductListingPage(WebDriver driver) {
		this.driver = driver;
		PageFactory.initElements(driver, this);
	}
	
	@FindBy(how = How.CSS, using = "button.single_add_to_cart_button") 
	private WebElement btn_AddToCart;
	
	@FindAll(@FindBy(how = How.CSS, using = ".noo-product-inner"))
	private List<WebElement> prd_List;	
	
	public void clickOn_AddToCart() {
		btn_AddToCart.click();
		Wait.untilJqueryIsDone(driver);
	}
	
	public void select_Product(int productNumber) {
		prd_List.get(productNumber).click();
	}
	
	public String getProductName(int productNumber) {
		return prd_List.get(productNumber).findElement(By.cssSelector("h3")).getText();
	}

}
  1. Now, using the getProductName() method, get the name and save it into the scenarioContext object in ProductPageSteps class for the below step:

Cucumber Step: And choose to buy the first item

ProductPageSteps.java

package stepDefinitions;

import cucumber.TestContext;
import cucumber.api.java.en.When;
import enums.Context;
import pageObjects.ProductListingPage;

public class ProductPageSteps {
	
	TestContext testContext;
	ProductListingPage productListingPage;
	
	public ProductPageSteps(TestContext context) {
		testContext = context;
		productListingPage = testContext.getPageObjectManager().getProductListingPage();
	}

	@When("^choose to buy the first item$")
	public void choose_to_buy_the_first_item() {
		String productName = productListingPage.getProductName(0); 
		testContext.scenarioContext.setContext(Context.PRODUCT_NAME, productName);
		
		productListingPage.select_Product(0);
		productListingPage.clickOn_AddToCart();		
	}
}

Step 3: Add a Step to Test for Product Name Validation

  1. The first step is to add a new Cucumber Step to Cucumber Test. As we want to verify the Product Name at the last page of the application, which is Confirmation Page, we add this step at the last:

Then verify the order details

I have added a generic step as Order Details, as I want you guys to practice more on it. As of now we will just add a Product Name validation in it, but I want you to verify multiple values in the test. Which can be these:

  • Product Details
  • Shipping Details
  • Discount Details

End2End_Test.feature

Share data between steps in Cucumber using Scenario Context

  1. Need to create a new Page Object for the Confirmation Page. Feel free to add as many objects you can and put validations for the same. For the simplicity of the test, I am just adding an object of the Product Name list.

3. Create a New Class and name it as ConfirmationPage, by right click on the pageObjects and select New >> Class.

ConfirmationPage.java

package pageObjects;

import java.util.ArrayList;
import java.util.List;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindAll;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
import org.openqa.selenium.support.PageFactory;

public class ConfirmationPage {
	WebDriver driver;
	
	public ConfirmationPage(WebDriver driver) {
		this.driver = driver;
	    PageFactory.initElements(driver, this);
	}
	
	@FindAll(@FindBy(how = How.CSS, using = ".order_item"))
	private List<WebElement> prd_List;	
	
	public List<String> getProductNames() {
		List<String> productNames = new ArrayList<>();
		for(WebElement element : prd_List) {
			productNames.add(element.findElement(By.cssSelector(".product-name")).getText());
		}
		return productNames;
	}
}

Note: Returning names of all the products displayed on the page.

  1. Add a new getConfirmationPage() method to get the Confirmation Page object in the PageObjectManager class.

PageObjectManager.java

package managers;

	import org.openqa.selenium.WebDriver;
	import pageObjects.CartPage;
	import pageObjects.CheckoutPage;
	import pageObjects.ConfirmationPage;
	import pageObjects.HomePage;
	import pageObjects.ProductListingPage;

public class PageObjectManager {
	private WebDriver driver;
	private ProductListingPage productListingPage;
	private CartPage cartPage;
	private HomePage homePage;
	private CheckoutPage checkoutPage;
	private ConfirmationPage confirmationPage;
	
	public PageObjectManager(WebDriver driver) {
		this.driver = driver;
	}
	
	public HomePage getHomePage(){
		return (homePage == null) ? homePage = new HomePage(driver) : homePage;
	}
	
	public ProductListingPage getProductListingPage() {
		return (productListingPage == null) ? productListingPage = new ProductListingPage(driver) : productListingPage;
	}
	
	public CartPage getCartPage() {
		return (cartPage == null) ? cartPage = new CartPage(driver) : cartPage;
	}
	
	public CheckoutPage getCheckoutPage() {
		return (checkoutPage == null) ? checkoutPage = new CheckoutPage(driver) : checkoutPage;
	}
	
	public ConfirmationPage getConfirmationPage() {
		return (confirmationPage == null) ? confirmationPage = new ConfirmationPage(driver) : confirmationPage;
	}
}

5. Create a New Class and name it as ConfirmationPageSteps, by right click on the stepDefinitions and select New >> Class.

ConfirmationPageSteps.java

package stepDefinitions;

import org.junit.Assert;

import cucumber.TestContext;
import cucumber.api.java.en.Then;
import enums.Context;
import pageObjects.ConfirmationPage;

public class ConfirmationPageSteps {
	TestContext testContext;
	ConfirmationPage confirmationPage;
	
	public ConfirmationPageSteps(TestContext context) {
		testContext = context;
		confirmationPage = testContext.getPageObjectManager().getConfirmationPage();
	}
	
	@Then("^verify the order details$")
	public void verify_the_order_details(){
		String productName = (String)testContext.scenarioContext.getContext(Context.PRODUCT_NAME);
		Assert.assertTrue(confirmationPage.getProductNames().stream().filter(x -> x.contains(productName)).findFirst().get().length()>0);		
	}

}

Explanation

verify_the_order_details()

  • To retrieve the value of the Product Name, just need to pass the Key to the getContext() method which can be accessed like testContext.scenarioContext.getContext(Key Name).
  • But, the method returns any object, so we need to cast it to the right type. If we mistakenly cast it to wrong type, the test will fail here. So, we must sure for what object we stored for what key. Or even this can be handled with in the code, but let's just avoid any complexity here for now.
  1. This is the last step, just add a JQuery waits after the submitting Order in CheckoutPage object.
public void clickOn_PlaceOrder() {
	btn_PlaceOrder.submit();
	Wait.untilJqueryIsDone(driver);
	Wait.untilPageLoadComplete(driver);
}

Run the Cucumber Test

Run as JUnit

Now we are all set to run the Cucumber test. Right Click on TestRunner class and Click Run As  >> JUnit TestCucumber will run the script the same way it runs in Selenium WebDriver and the result will be shown in the left-hand side project explorer window in JUnit tab.

You will notice that after executing all the steps, the execution will come in the hooks and it will execute quitDriver().

Project Explorer

Share data between steps in Cucumber using Scenario Context

Handle Ajax call Using JavaScriptExecutor in Selenium?
Handle Ajax call Using JavaScriptExecutor in Selenium?
Previous Article
Cucumber Reports
Cucumber Reports
Next Article
Lakshay Sharma
I’M LAKSHAY SHARMA AND I’M A FULL-STACK TEST AUTOMATION ENGINEER. Have passed 16 years playing with automation in mammoth projects like O2 (UK), Sprint (US), TD Bank (CA), Canadian Tire (CA), NHS (UK) & ASOS(UK). Currently, I am working with RABO Bank as a Chapter Lead QA. I am passionate about designing Automation Frameworks that follow OOPS concepts and Design patterns.
Reviewers
Virender Singh's Photo
Virender Singh

Similar Articles

Feedback