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 not
MyProfilePage`.
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; } ...}