Search code examples
springcucumber-jvm

Cucumber Spring and class configuration


I'm struggling with Cucumber and Spring configuration. I'm writing selenium framework using Page Object Pattern, with BrowserFactory.

When I use @ComponentScan, @Component and @Autowire annotations everything works fine, but when I want to create a bit more complicated bean with @Bean annotation (BrowserFactory which registers few browser drivers) in @Configuration class it does not work, during debug I'm getting nulls on every single variable I'm trying to Autowire.

I'm using Spring 4.2.4, all cucumber dependencies in version 1.2.4.

Config:

@Configuration
public class AppConfig {

@Bean
@Scope("cucumber-glue")
public BrowserFactory browserFactory() {
  BrowserFactory browserFactory = new BrowserFactory();
  browserFactory.registerBrowser(new ChromeBrowser());
  browserFactory.registerBrowser(new FirefoxBrowser());
  return browserFactory;
}

@Bean(name = "loginPage")
@Scope("cucumber-glue")
public LoginPage loginPage() throws Exception {
  return new LoginPage();
}

@Bean(name = "login")
@Scope("cucumber-glue")
public Login login() {
  return new Login();
}
}

POP:

public class LoginPage extends Page {

  public LoginPage() throws Exception {
   super();
  }
  ...
}

Page:

public class Page {

  @Autowired
  private BrowserFactory browserFactory;

  public Page() throws Exception{
    ...
  }
}

Login:

public class Login {

  @Autowired
  private LoginPage loginPage;

  public Login(){}
    ...
}

Steps:

@ContextConfiguration(classes = {AppConfig.class})
public class LoginSteps {

  @Autowired
  Login login;

  public LoginSteps(){
  }

  @Given("^an? (admin|user) logs in$")
  public void adminLogsIn(Login.User user) throws Exception {
    World.currentScenario().write("Logging in as " + user + "\n");
    login.as(user);
  }
}

Error:

cucumber.runtime.CucumberException: Error creating bean with name 'LoginSteps': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: Login LoginSteps.login; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'login': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private LoginPage Login.loginPage; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginPage' defined in AppConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [LoginPage]: Factory method 'loginPage' threw exception; nested exception is java.lang.NullPointerException

And now for the fun part... BrowserFactory in World class is properly Autowired!!

World:

public class World {

  @Autowired
  private BrowserFactory browserFactory;
  ...
}

Solution

  • So I'll answer my own question:)

    Issue was that I was calling BrowserFactory inside of Page constructor. Looks like this bean was not yet created and was causing NPEs.

    In order to fix that I:

    • Added @Lazy annotation to configuration (all elements that use this configuration, both defined in that class and those which will be found by Scan will be created as Lazy)
    • Moved call to Browser Factory to @PostConstruct method

    Two more things to increase readability of Spring config:

    • Added @ComponentScan to configuration
    • Classes with no constructor parameters are annotated with @Component and @Scope("cucumber-glue") annotation so bean creation can be removed from AppConfig.class