3

I'm having trouble with NullPointerExceptions when I try using static methods in a page object. If I do it with non-static methods, it works fine.

Non-static version:

public class ComplaintPage {

    private ExtendedWebDriver driver;

    @FindBy(css = "[data-selector=date-received-complaint]")
    public WebElement dateComplaintReceoved;

    public ComplaintPage() {
        driver = Browser.extendedDriver();
        PageFactory.initElements(driver, this);
    }

    public void setComplaintDate() {
        dateComplaintReceoved.sendKeys(LocalDate.now().toString());
    }
}

Calling code:

ComplaintPage complaintPage = new ComplaintPage;
complaintPage.setComplaintDate();

This works fine. The date field is set.

Static version

public class ComplaintPage {

    private static ExtendedWebDriver driver;

    @FindBy(css = "[data-selector=date-received-complaint]")
    public static WebElement dateComplaintReceoved;

    public ComplaintPage() {
        driver = Browser.extendedDriver();
        PageFactory.initElements(driver, this);
    }

    public void static setComplaintDate() {
*       dateComplaintReceoved.sendKeys(LocalDate.now().toString());
    }
}

Calling code:

ComplaintPage.setComplaintDate();

This does not work, and results in a java.lang.NullPointerException on the line marked with "*" (the line accessing the WebElement).

I kind of like using static methods like this in test, since I don't really see a problem with it, and it makes the code even more easy to read. And I've done it before, in C#/VS, but for some reason I'm missing something important here.

1 Answers1

7

NullPointerException is thrown because of how PageFactory works. You see, when you create an instance of ComplaintPage class, you are invoking its constructor:

public ComplaintPage() {
        driver = Browser.extendedDriver();
        PageFactory.initElements(driver, this);
    }

The constructor calls initElements method of PageFactory class. This method initializes all of WebElement and List<WebElement> fields with Java Reflection API. It basically changes the default null values to implementations of the interface. It also provides sort of Lazy instantiation of the WebElement which means - WebElements are found (looked for?) only when needed - when you invoke operations on them.

When you created static methods and static WebElements - you did not call the constructor of the class, which lead to NOT invoking the initElements method.

All of the elements annotated with @FindBy were not initialized. That's why it's not a good idea to use PageFactory with static methods.

Instead of using PageFactory you can just find the element with classic driver.findElement inside the static method.

    public void static setComplaintDate(WebDriver driver) {
        driver.findElement(By.cssSelector("[data-selector=date-received-complaint]")).sendKeys(LocalDate.now().toString());
    }
Fenio
  • 3,528
  • 1
  • 13
  • 27
  • 1
    Of course! How did I not see that. I prefer the PageFactory way of specifying and creating WebElements, so I'll have to stick with non-static methods then. –  May 08 '19 at 13:46
  • @Fenio, I did not get actually. Even if WebElement is of static type and here as well we are using PageFactory.initElements(driver, this); which would initialize the static variables ? Is something different going on here. – TheSociety May 08 '19 at 15:09
  • He didn't call the constructor – Fenio May 08 '19 at 16:15
  • @TheSociety and if the OP has to call the constructor to initialize WebElements then creating a static method misses its purpose. – Fenio May 09 '19 at 04:50
  • Do you mean to say, by calling constructor, we would be able to initialize static variable but in page factory we shall follow non static. – TheSociety May 09 '19 at 04:52
  • @TheSociety Sort of. Page Factory is mean to be non-static. It's based on Objects (not only Page Objects) and their relationship with other Objects. Using non-static methods allows you to take the full advantage of how PageFactory works. Using static elements will lead to code complexity, creating workarounds and you can't really pass one object to another. Additionally, it might cause issues with parallel testing. Each test should create its own object and operate on the same elements but in different states – Fenio May 09 '19 at 04:56
  • @Fenio, thank you so much for providing this detailed information. Is there any place like book etc where we can explore Page Factory concepts deeply. – TheSociety May 09 '19 at 05:01
  • @TheSociety Unfortunately, I don't know. I just read the source code of Selenium `PageFactory` class and `Decorator` pattern. Then, to understand it more deeply I extended the existing solution to provide support for my custom controls. Instead of using `@FindBy` to get `WebElement` (or List) I can now use `@FindBy` to get my custom classes like `TextField`, `Button` etc. This gave me a deeper understanding of how `PageFactory` works :) – Fenio May 09 '19 at 05:05