Project throwing unsatisfied dependency issue when using picocontainer

885 Views Asked by At

Having some trouble with implementing Cucumber with a test I'm trying to set up. Managed to get the test working Selenium using this as a guide: https://medium.com/@cbuelvasc/test-automation-in-kotlin-with-selenium-testng-and-page-object-model-e2252dbc492a

When I try and add a Feature over it, and step definitions - it complains about a missing dependency for picocontainer:

org.picocontainer.injectors.AbstractInjector$UnsatisfiableDependenciesException: pages.LoginPage has unsatisfied dependency 'interface org.openqa.selenium.WebDriver' for constructor 'public pages.LoginPage(org.openqa.selenium.WebDriver)' from org.picocontainer.DefaultPicoContainer@501edcf1:1<|

This is the Feature file:

Feature: Login
  Scenario: A user can log in when they provide valid credentials
    When a valid username is entered in the username field

This LoginPage class, with element locators and a single step definition which I was hoping to use...

import io.cucumber.java.en.When
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebElement
import org.openqa.selenium.support.FindBy
import org.openqa.selenium.support.PageFactory

class LoginPage(driver: WebDriver) {
    @FindBy(css = "input.ant-input.input.labeled-input__input.with-input-styles")
    private val emailAddressField: WebElement? = null

    @FindBy(css = "input.ant-input.input.password-input__input.with-input-styles")
    private val passwordField: WebElement? = null

    @FindBy(css = "button.ant-btn.ant-btn-primary.ant-btn-lg.button.button_type_primary.login-form__submit-button")
    private val loginButton: WebElement? = null

    init {
        PageFactory.initElements(driver, this)
    }

    // Input fields
    @When("a valid username is entered in the username field")
    fun addUsername() = emailAddressField?.sendKeys("[value]")


    fun addPassword() = passwordField?.sendKeys("[value]")

    // Buttons
    fun login() = loginButton?.click()
}

This is the Abstract class for setup:

import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeDriver
import org.testng.annotations.AfterTest
import org.testng.annotations.BeforeTest
import util.UtilResources
import java.net.URI
import java.util.concurrent.TimeUnit

abstract class TestBase {
    lateinit var driver: WebDriver
        private set

    @BeforeTest
    fun setup() {
        System.setProperty(
            UtilResources.getProperties("nameDriver"),
            UtilResources.getProperties("pathDriver") + UtilResources.getProperties("exeDriver")
        )
        driver = ChromeDriver()
        driver.manage()?.timeouts()?.implicitlyWait(10, TimeUnit.SECONDS)
        driver.manage()?.window()?.maximize()
        driver.get(URI(UtilResources.getProperties("pageURL")).toString())
    }

    @AfterTest
    fun driverClose() {
        driver.close();
    }
}

The pom file includes the dependencies (but every time I try to include it - StackOverflow marks as Spam)

Full error:

Step failed
org.picocontainer.injectors.AbstractInjector$UnsatisfiableDependenciesException: pages.LoginPage has unsatisfied dependency 'interface org.openqa.selenium.WebDriver' for constructor 'public pages.LoginPage(org.openqa.selenium.WebDriver)' from org.picocontainer.DefaultPicoContainer@501edcf1:1<|

Wanted to run the test as the BDD that was provided. Used the initial guide(linked) as a proof-of-concept, writing it in Kotlin, and tested fine with @Test testng route. When attempting to add step definitions over the top; getting an UnsatisfiableDependenciesException, even though all classes have the import the error is referring to. Can someone please help? Have I missed some fundamental simple thing?

1

There are 1 best solutions below

0
On

Your login page takes interface as the construction parameter. While this is the proper pattern for DI in general, Cucumber integrates with PicoContainer in simplified way.

So theoretically to make your approach work with any DI framework you have to have a mapping somewhere saying like "Okay DI framework, if you meet somewhere an instance of WebDriver you should instantiate that field or method parameter with this particular class: ChromeDriver"

Cucumber integration with PicoContainer out of the box implies that it just searches for a given class and if it is instantiatable, it is okay. But since WebDriver itself cannot be instantiated PicoContainer does not know what to put to that particular place.

The solution for PicoContainer would be to create a wrapper class that would be delegating calls to WebDriver methods to underlying particular implementation of that interface. What the implementation would be can be controlled by a parameter (for example).

N.B. - that wrapper should initialize the underlying implementation in a lazy way and implement Disposable interface that ensures the proper termination of WebDriver.

P.S. - You can find more detailed explanation with example in Java (can be trivially converted to Kotlin case) here.