</>

Technology

Cucumber

Difficulty

Advanced

Interview Question

How do you implement Dependency Injection in Cucumber using PicoContainer?

Answer

Dependency Injection with PicoContainer in Cucumber

Why DI Is Needed

When you split step definitions across multiple classes (LoginSteps, CheckoutSteps, etc.), each class needs access to the same WebDriver instance and shared state. Without DI, you would use static variables — which are not thread-safe and cause issues in parallel execution.

PicoContainer solves this by creating one instance per scenario of each class and injecting it automatically.

Step 1: Add Dependency

XML
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-picocontainer</artifactId>
    <version>7.15.0</version>
    <scope>test</scope>
</dependency>

Step 2: Create the Shared Context

Java
package context;

import org.openqa.selenium.WebDriver;

public class ScenarioContext {
    // Shared driver
    public WebDriver driver;

    // Shared test data between step classes
    public String authToken;
    public String orderId;
    public String userId;

    // Store data dynamically
    private Map<String, Object> context = new HashMap<>();

    public void set(String key, Object value) {
        context.put(key, value);
    }

    public Object get(String key) {
        return context.get(key);
    }
}

Step 3: Inject into Step Definitions

Java
public class LoginSteps {
    private final ScenarioContext ctx;

    // PicoContainer sees this constructor and auto-injects ScenarioContext
    public LoginSteps(ScenarioContext ctx) {
        this.ctx = ctx;
    }

    @Given("the user logs in as {string}")
    public void login(String username) {
        ctx.driver.findElement(By.id("user")).sendKeys(username);
        ctx.driver.findElement(By.id("login")).click();
        ctx.authToken = getTokenFromCookie();  // store for other steps
    }
}

public class CheckoutSteps {
    private final ScenarioContext ctx;

    public CheckoutSteps(ScenarioContext ctx) {
        this.ctx = ctx;
    }

    @When("the user places an order")
    public void placeOrder() {
        // Uses same driver instance
        // Uses authToken set by LoginSteps
        apiClient.createOrder(ctx.authToken, productId);
    }
}

public class Hooks {
    private final ScenarioContext ctx;

    public Hooks(ScenarioContext ctx) {
        this.ctx = ctx;
    }

    @Before
    public void setUp() {
        ctx.driver = new ChromeDriver();  // one driver per scenario
    }

    @After
    public void tearDown() {
        ctx.driver.quit();
        // ctx is discarded — fresh context for next scenario
    }
}

How PicoContainer Works Internally

CODE
1. Scenario starts
2. PicoContainer creates ONE instance of ScenarioContext
3. Finds all step definition classes referenced in the scenario
4. Creates ONE instance of each step class, injecting the SAME ScenarioContext
5. All step classes share the same driver, same state
6. Scenario ends → PicoContainer discards all objects
7. Next scenario → fresh objects created

Without DI vs With DI

Java
// WITHOUT DI (anti-pattern)
public class LoginSteps {
    static WebDriver driver;  // ← static = not thread-safe
}

// WITH DI (correct)
public class LoginSteps {
    private final ScenarioContext ctx;  // ← injected = thread-safe
    public LoginSteps(ScenarioContext ctx) { this.ctx = ctx; }
}

Other DI Frameworks Supported

FrameworkDependency
PicoContainercucumber-picocontainer (simplest)
Springcucumber-spring
Guicecucumber-guice
CDIcucumber-cdi2

PicoContainer requires zero configuration — just add the dependency and use constructors.

Follow AutomateQA

Related Topics