Search:

Implementing Dependency Injection with Java-Selenium-Cucumber

Dependency Injection for Page objects

Implementing Dependency Injection with Java-Selenium-Cucumber

While coding an automation framework from scratch or on an existing one, you should take care of some basics. The framework should be maintainable, easy to understand, avoid coding duplicates, and quickly adapt to changes. To overcome these basics, you should follow some design principles and techniques in your framework. This article will show you one of these design patterns-Page Object Model(POM)-and explain its details.

Page Object Model

POM is one of the most popular approaches to ensure your projects’ quality. The goal of using page objects is to avoid code duplication and also make the projects maintainable. The POM approach advocates defining your pages as objects and using these objects in your automation steps. This approach puts all the required elements or methods related to the page in one class. This consolidation creates a centralized place to find related items together. In the future, if any changes are needed, you can easily navigate this class and adjust it as needed. No need to touch anywhere else in the rest of the framework or projects that use this framework. 

Here is one example of the usage of the POM approach:


    @FindBy(id = "some-username-id")
    public WebElement usernameBox;

    @FindBy(xpath = "//some-password-xpath")
    public WebElement passwordBox;

    @FindBy(css = ".some-login-css")
    public WebElement loginButton;

    public void login(String username, String password) {
        usernameBox.sendKeys(username);
        passwordBox.sendKeys(password);
        loginButton.click();
    }

    

As you see above, while you are writing your steps, you don’t need to care about implementing your pages anymore. You will be just using related methods, and upcoming page changes don’t affect your actions. You will adjust the pages as required and reuse them.

Using Pages Object Model

After creating your page classes, you need to create instances to use them in the steps definition class. You need to define an object with a new keyword in the related steps or define it as a global variable.

Here is an example of the usage the pages objects:


    @Given("user should be able to login {string} {string}")
    public void user_should_be_able_to_login(String username, String password){ 
        Homepage homepage = new Homepage();
        homepage.login(username, password);
    }
    

    private Homepage homepage = new Homepage();

    @Given("user should be able to login {string} {string}")
    public void user_should_be_able_to_login(String username, String password) {
        homepage.login(username, password);
    }
    

As you can see, using POM is handy and makes your job very easy. But there is a design problem with the example above. Defining variables with the new keyword makes your code tightly coupled. Your objects become dependent on that class if you create your objects with the new keyword, and it is hard to implement any upcoming changes to your steps class. So, we need a solution to handle this tightly coupled problem. 

There is a design principle to rescue us and make our codes clear and maintainable to avoid design problems. Let’s talk about Inversion of Control (IoC) and investigate dependency injection, one way to implement IoC.

Inversion of Control

Inversion of control is a software design principle used to invert different kinds of controls to achieve loose coupling. Here, controls refer to any additional responsibilities a class has, other than its primary responsibility. IoC includes control over an application’s flow and control over an object creation or dependent object creation and binding.

Dependency injection

Dependency injection (DI) is one of the design patterns and implementations of the IoC. DI takes the dependent object creation outside of the class and provides required objects in different ways. Using DI, you move the creation and binding of the dependent objects outside of the class that depends on them.

There are many DI types that you can use, such as Constructor Injection, Property Injection, and Method Injection. In this article, I will be talking about Constructor Injection.

Constructor Injection

In constructor injection, the injector provides the required dependency through the constructor. Some third-party tools would help you to implement DI on a constructor. I will be talking about Cucumber Picocontainer dependency, one of the third-party dependencies, to implement constructor injection. 

Cucumber Picocontainer

Cucumber Picocontainer is a third-party framework for providing required dependencies in your classes. This framework will make your classes loosely coupled and take all responsibility for injecting required dependencies. You can use it in your framework to achieve loose coupling. Now let’s implement DI in the automation framework with Cucumber Picocontainer.

Usage of Cucumber Picocontainer

To be able to use Cucumber Picocontainer, you should add this dependency in your classpath. Make sure that you have added to the same version with JUnit and cucumber dependencies. Otherwise, you will get an exception while trying to use Picocontainer dependency. 


<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-picocontainer -->
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-picocontainer</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
</dependency>

After adding the above dependency, you should create a constructor in your page definition class and pass the required objects there.


public class HomePageStepDefinitions {

    private Homepage homepage;

    public HomePageStepDefinitions(Homepage homepage) {
        this.homepage = homepage;
    }

}
  

    @Given("user should be able to login {string} {string}")
    public void user_should_be_able_to_login(String username, String password) {
        homepage.login(username, password);
    }
    

As you see in the example above, you are only responsible for creating constructors and reference names. The rest of the responsibility for providing objects to your class belongs to third-party dependency, Cucumber Picocontainer.

After implementation, you are only responsible for creating the constructor and defining what you need in the class. Picocontainer becomes the main responsible for the rest of the assigning and injection. If any upcoming changes are required, you will be responsible for making changes only on the constructor, and you won’t take care of the rest.

Conclusion

I have explained how to implement Dependency Injection to your automation framework and use it in the steps class. I have covered constructor injection and its usage in this article.

Finally, we can say you can move your automation frameworks to one step forward with implementing design approaches like dependency injection.

Resources:

https://cucumber.io/docs/cucumber/state/

https://en.wikipedia.org/wiki/Inversion_of_control

Click to download the Selenium - Java Cheatsheet

Selcuk Temizsoy

Experienced Software Test Automation Engineer working in different industries. Software Test Consultant at kloia