Cucumber PicoContainer ====================== Use PicoContainer to provide dependency injection to steps. Add the `cucumber-picocontainer` dependency to your `pom.xml` and use the [`cucumber-bom`](../cucumber-bom/README.md) for dependency management: ```xml [...] io.cucumber cucumber-picocontainer test [...] ``` ## Step dependencies PicoContainer uses constructor dependency injection to create instances of step definition classes and their dependencies. In the example bellow, to create an instance of `StepDefinition` an instance of `Belly` is needed. So `Belly` is a dependency of `StepDefinition`. `Belly` has no dependencies and can be created with the default constructor. Once an instance of `Belly` has been created, this can be used to create an instance of `StepDefinition`. This instance is then used to invoke the Given/ When/Then methods on. Note: _When using `cucumber-pico`, classes declaring step definitions must be public and have a public constructor._ ```java package com.example.app; import java.util.List; public class Belly { private final List contents; public void setContents(List contents){ this.contents = contents; } public List getContents(){ return contents; } } ``` ```java package com.example.app; import cucumber.api.java.en.Given; import cucumber.api.java.en.Then; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; public class StepDefinition { private final Belly belly; public StepDefinitions(Belly belly) { this.belly = belly; } @Given("I have {int} {word} in my belly") void I_have_n_things_in_my_belly(int n, String what) { belly.setContents(Collections.nCopies(n, what)); } @Then("there are {int} cukes in my belly") void checkCukes(int n) { assertEquals(belly.getContents(), Collections.nCopies(n, "cukes")); } } ``` ## Step scope and lifecycle All step classes and their dependencies will be recreated for each scenario, even if the scenario in question does not use any steps from that particular class. To improve performance, it is recommended to lazily create expensive resources. ```java public class LazyWebDriver implements Webdriver { private final Webdriver delegate; private Webdriver getDelegate() { if (delegate == null) { delegate = new ChromeWebDriver(); } return webdriver; } @Override public void doThing() { getDelegate().doThing(); } ... } ``` Step classes or their dependencies which own resources requiring cleanup should implement `org.picocontainer.Disposable` as described in [PicoContainer - Component Lifecycle](http://picocontainer.com/lifecycle.html). These hooks will run after any Cucumber after hooks. ## Customizing PicoContainer Cucumber `PicoFactory` is intentionally not open for extension or customization. If you want to customize your dependency injection context, it is recommended to provide your own implementation of `io.cucumber.core.backend.ObjectFactory` and make it available through SPI. However it is possible to configure additional PicoContainer `Provider`s. For example, some step definition classes might require a database connection as a constructor argument. ```java package com.example.app; import java.sql.*; import io.cucumber.picocontainer.CucumberPicoProvider; import org.picocontainer.injectors.Provider; @CucumberPicoProvider public class DatabaseConnectionProvider implements Provider { public Connection provide() throws ClassNotFoundException, ReflectiveOperationException, SQLException { // Connecting to MySQL Using the JDBC DriverManager Interface // https://dev.mysql.com/doc/connector-j/en/connector-j-usagenotes-connect-drivermanager.html Class.forName("com.mysql.cj.jdbc.Driver").getDeclaredConstructor().newInstance(); return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "mydbuser", "mydbpassword"); } } ```