The Error
java.lang.NullPointerException:
Cannot invoke "org.openqa.selenium.WebElement.sendKeys(java.lang.CharSequence[])"
because "this.username" is null
Your locator is right. Your element exists. And it's null.
What It Means
If you try to access or interact with a web element without initializing it through Page Factory (or another method), you get a NullPointerException — the exception thrown when performing operations on an object that hasn't been instantiated or assigned a value.
The subtlety: @FindBy is just an annotation. It's metadata. It doesn't do anything on its own.
@FindBy(id = "username")
private WebElement username; // ← this is null. Right now. Always.
Something has to read that annotation and populate the field. That something is PageFactory.initElements(). Without it, your field stays exactly as Java left it: null.
The Fix: initElements ⭐
public class LoginPage {
WebDriver driver;
@FindBy(id = "username") private WebElement username;
@FindBy(id = "password") private WebElement password;
@FindBy(id = "login") private WebElement loginBtn;
public LoginPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this); // ⭐ THE LINE
}
public void login(String user, String pass) {
username.sendKeys(user);
password.sendKeys(pass);
loginBtn.click();
}
}
That one line is what makes @FindBy mean anything. Proper initialization of Page Object elements before use is essential.
The Causes
Missing initElements() ⭐
The above. ~60% of cases.
Driver is null
// ❌ Field never assigned
public class BaseTest {
WebDriver driver; // null
@BeforeMethod
public void setUp() {
WebDriver driver = new ChromeDriver(); // ⚠️ NEW local variable!
}
}
// ✅ Assign the field
@BeforeMethod
public void setUp() {
driver = new ChromeDriver();
}
The shadowed-variable bug: you declared a local driver instead of assigning the field. Compiles fine. NPEs at runtime.
Page Object created before the driver
// ❌ driver is still null here
LoginPage login = new LoginPage(driver);
driver = new ChromeDriver();
// ✅
driver = new ChromeDriver();
LoginPage login = new LoginPage(driver);
ThreadLocal driver not set in parallel
private static ThreadLocal<WebDriver> tlDriver = new ThreadLocal<>();
public static WebDriver getDriver() {
return tlDriver.get(); // null on a thread that never called set()
}
ThreadLocal.get() returns null on any thread where set() wasn't called — so a @BeforeSuite that sets the driver won't help @Test methods on other threads. Set it in @BeforeMethod.
Manual findElement result never assigned
WebElement btn; // null
if (someCondition) {
btn = driver.findElement(By.id("x"));
}
btn.click(); // 💥 if the condition was false
PageFactory vs Plain Locators — the Honest Take
// PageFactory style
@FindBy(id = "username") private WebElement username;
// requires initElements(), lazily re-locates on each call
// Plain By style — ⭐ what most modern frameworks use
private By username = By.id("username");
public void enterUser(String u) {
driver.findElement(username).sendKeys(u);
}
// no init needed, no NPE, no staleness
The By approach can't throw this NPE at all — there's nothing to initialize. It also sidesteps StaleElementReferenceException, because you resolve the element fresh every call.
If you're hitting NPEs and stale-element errors in the same framework, that's a strong signal to move from @FindBy to By.
Quick Diagnosis
| The NPE names… | Cause | Fix |
|---|---|---|
A @FindBy field |
No initElements() |
Add it to the constructor |
driver |
Driver never assigned | Check for a shadowed local variable |
| A field in a parallel run | ThreadLocal not set() on this thread |
Set in @BeforeMethod |
| A conditionally-assigned element | The branch never ran | Assign unconditionally |
A @FindBy field only sometimes |
Page Object built before the driver | Reorder |
The Interview Answer
"In a Page Object framework,
@FindByis only an annotation — the WebElement fields stay null untilPageFactory.initElements(driver, this)populates them, usually in the constructor. Miss that and every element throws NullPointerException. The other common cause is a null driver — often a shadowed local variable in@BeforeMethod, or a ThreadLocal driver never set on that thread in parallel runs. Personally I prefer storingBylocators over@FindByWebElements — there's nothing to initialize, so this class of NPE and stale-element issues can't occur."
FAQs
Why is my @FindBy element null?
Because @FindBy is only metadata. You must call PageFactory.initElements(driver, this) — normally in the Page Object constructor — for the fields to be populated.
Where should initElements go?
In the Page Object's constructor, right after assigning the driver.
Why is my driver null in @BeforeMethod?
Usually a shadowed variable — WebDriver driver = new ChromeDriver(); creates a local, leaving the field null. Drop the type:
driver = new ChromeDriver();
Why NPE only in parallel runs?
ThreadLocal.get() returns null on threads where set() was never called. Initialize per test method, not per suite.
Can I avoid this entirely?
Yes — store By locators instead of @FindBy WebElements. Nothing to initialize, and no staleness either.
Related
- Frameworks & Page Object Model — POM done properly
- StaleElementReferenceException — the sibling problem
- Parallel Execution & Grid — ThreadLocal drivers
- Java Exception Handling