Java-Cucumber tests using spring dependency injection throws NullPointerException when Feature spans Step classes Java-Cucumber tests using spring dependency injection throws NullPointerException when Feature spans Step classes selenium selenium

Java-Cucumber tests using spring dependency injection throws NullPointerException when Feature spans Step classes


You have two page classes MyAccountsPage and MyProfilePage. While both extend BasePageWeb and thus any instances of the MyAccountsPage and MyProfilePage are are also instances BasePageWeb they are not the same instance!

This can be quite confusing initially because usually there is only a single instance of each class and we treat the instance and the class as if they were the same thing. Rather think of the class as a template from which many instances can be made.

Now if you attach the debugger and inspect the pages before they are used you should see something like this:

MyAccountsPage@1001 - WebDriver driver = null  <--- field inherited from BasePageWeb - other fieldsMyProfilePage@1002 <--- different memory address, so different instance! - WebDriver driver = null   <--- field inherited from BasePageWeb  - other fields

So when you setup the WebDriver using the steps in AccountsSteps, the WebDriver is setup in MyProfilePagebut notMyProfilePage`.

MyAccountsPage@1001 - WebDriver driver = Webdriver@1003  <-- This one was set. - other fieldsMyProfilePage@1002 - WebDriver driver = null   <--- This one is still null. - other fields

So when you try use the ProfileSteps that try to use MyProfilePage you end up with an null pointer exception because the instance of the WebDriver in MyProfilePage was never setup.

There are a few solutions here, but they all come down to keeping the webdriver in a single instance by making BasePageWeb a component and using composition instead of inheritance.

@Component@ScenarioScopepublic class BasePageWeb { ...}
public class AccountsSteps {    private final BasePageWeb basePageWeb;    private final MyAccountsPage page;    @Autowired    public AccountsSteps(BasePageWeb basePageWeb, MyAccountsPage page){        this.basePageWeb = basePageWeb;        this.page = page;    }    @Given("Log into {string} on {string} as {string} with {string} using {string}")    public void logIntoOnAsWithUsing(String app, String env, String user, String pass, String browser) {        basePageWeb.loadAny(env, app, browser);        page.sendUsername(user);        page.sendPassword(pass);        page.loginButtonClick();    }    ....
@Component@ScenarioScopepublic class MyAccountsPage {    private final BasePageWeb basePageWeb;    public MyAccountsPage(BasePageWeb basePageWeb) {        this.basePageWeb = basePageWeb;    }    ...}
@Component@ScenarioScopepublic class MyProfilePage {    private final BasePageWeb basePageWeb;    public MyProfilePage(BasePageWeb basePageWeb) {        this.basePageWeb = basePageWeb;    }    ...}