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");
}
}
```