Introduction:
Dependency Injection can be used in UI Automation with Selenium Cucumber Framework. In this blog, I will try to explain how I have implemented and used Dependency Injection for one of my projects.
So, Let's start with
What is Dependency Injection?
Dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on. It is a programming technique that makes a class independent of its dependencies.
How we can make a class independent of these dependencies? We inject these dependencies through a constructor.
What is the use of Dependency Injection in UI automation?
When we use Page Object Model for our UI automation using the Cucumber BDD framework, we create separate pages (classes) for each module or webpage to store Web elements on that page. We create many step definition classes in the project and every time we need to create the page objects which are to be used in these step definitions.
Also, we have to make WebDriver, Static since the same WebDriver instances need to be shared between classes. Static Web Driver is not a good choice at the time of parallel execution.
In these situations, we can use Dependency Injection to:
1) Create the same instance of a WebDriver through all the classes without declaring it to be static
2) Create all the page objects in one class and inject them through all the step definition classes.
Cucumber Pico-container dependency helps to implement Dependency Injection.
Start-to-end step-by-step procedure to incorporate Dependency Injection :
1) Add Pico-container dependency in POM.xml along with all other dependencies.
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>7.5.0</version>
<scope>test</scope>
</dependency>
2) Create a Base class that will have all the basic methods such as Launching the Browser.
public class BaseClass {
ConfigReader cfr = new ConfigReader();
protected Properties prop = cfr.loadConfig();
public WebDriver launchBrowser() throws Exception {
String browser = prop.getProperty("Browser");
WebDriver driver;
if(browser.equalsIgnoreCase("Edge")){
WebDriverManager.edgedriver().setup();
driver = new EdgeDriver();
}
else if(browser.equalsIgnoreCase("Chrome")) {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
}
else {
driver = new FirefoxDriver();
}
driver.manage().timeouts().implicitlyWait(20000, TimeUnit.MILLISECONDS);
driver.manage().window().maximize();
return driver;
}
}
3) Create one class (I called it as TextContext. Name can be decided as per your choice) to initialize the page objects and add a getter to call these objects in any of the step definition classes. Also, create a getter and setter for the Web driver as shown below:
public class TestContext {
protected WebDriver driver;
public LogInPage loginObj;
public HomePage homeObj;
public WebDriver getDriver() {
return driver;
}
public void setDriver( WebDriver driver) {
this.driver = driver;
}
public HomePage getHomeObj() {
return homeObj;
}
public void InitializePageObject(WebDriver driver) {
homeObj = new HomePage(driver);
loginObj = new LogInPage(driver);
}
}
4) Create Pages as required for the project extending the BaseClass and create a constructor to initialize WebDriver to write all the driver actions. Page Object classes will have all Web Elements for that web page and methods to perform actions.
public class LogInPage extends BaseClass {
WebDriver driver;
public LogInPage(WebDriver d) {
this.driver = d;
}
private By loginbtn = By.xpath("//button[@id='login']");
private By Username = By.xpath("//input[@id='username']");
private By Password = By.xpath("//input[@id='password']");
public String getLoginPageTitle() {
return action.getTitle(driver);
}
}
5) Create Step definitions classes as per requirement. In all the step definition classes we have to create a constructor to inject the TestContext class object.
Pico-container dependency injection mechanism of Cucumber will check for any constructor that is there in any class (step definition). If it finds the constructor it will inject the object into that class.
Also, Pico-container dependency makes sure that in each class the same instance of the object of TextContext class is injected. It will get injected at the start of the execution of the class which needs those objects.
This will now allow us to access and use all the methods and objects in TextContext class in any of our Step Definition files. This intern will allow us to use the methods in Page Objects as and when we need to call them.
public class LogInStepDefs extends BaseClass {
TestContext testcontext;
public LogInStepDefs(TestContext testcontext) {
this.testcontext = testcontext;
}
@Given("User is on the Login Page")
public void user_is_on_the_login_page() {
// System.out.println("Opened Browser");
// WebDriver driver = null;
// try {
// driver = launchBrowser();
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
testcontext.getDriver().get(prop.getProperty("baseUrl"));
}
@When("User clicks the Login button after entering invalid {string}")
public void user_clicks_the_login_button_after_entering_invalid(String invalidPassword) throws InterruptedException {
testcontext.getLoginObj().shouldShowInvalidPassword(invalidPassword);
testcontext.getLoginObj().clickonLogin();
}
@Then("User should receive the message {string}")
public void user_should_receive_the_message(String expTitle) {
String ActTitle = testcontext.loginObj.getInvalidLoginErrorMessage();
System.out.println("Title got loaded is " + ActTitle);
Assert.assertEquals(ActTitle, expTitle);
System.out.println("User is on login Page");
}
We can see below, the other step definition class which is created for the same project. The same constructor needs to be created and the TextContext class object will be injected in this step definition class too.
public class TitleStepDefs extends BaseClass{
TestContext testcontext;
public TitleStepDefs(TestContext testcontext) {
this.testcontext = testcontext;
}
@Given("User is on the browser")
public void user_is_on_the_browser() {
testcontext.getDriver().get(prop.getProperty("baseUrl"));
//testcontext.InitializePageObject(testcontext.getDriver());
System.out.println("After Given");
}
@When("User landed on the Home page after logging into the LMS website")
public void user_landed_on_the_home_page_after_logging_into_the_lms_website() throws Exception {
testcontext.getLoginObj().shouldShowLMShomePageAfterLogin();
testcontext.getLoginObj().clickonLogin();
System.out.println("After When");
}
@Then("verify that title of the page is {string}")
public void verify_that_title_of_the_page_is(String TitleExp) {
String TitleAct = testcontext.getHomeObj().getLoginPageTitle();
System.out.println("Actual title =" +TitleAct);
System.out.println("Expected title =" +TitleExp);
Assert.assertEquals(TitleAct, TitleExp);
}
}
Comments