skip to content

In Selenium, what does LoadableComponent.get() do before and after calling load()?

level: juniorimportance: should knowfreq 45%

answer

  1. Readiness expressed inside the screen object
  2. One public method, two abstract ones
  3. Check first, act only on failure
  4. load() once, isLoaded() up to twice
  5. The second check is not caught

basics

~10 s

It calls isLoaded() first; if that throws an Error the screen is not ready, so get() calls load() exactly once, then calls isLoaded() a second time. A second failure escapes to the caller.

solid answer

~40 s

`LoadableComponent<T>` in `org.openqa.selenium.support.ui` is an abstract base for a screen object, and its only public method is `get()`. The body is fixed by the base class: it calls your `isLoaded()`; if that returns normally the screen is already open, so `get()` returns `this` and `load()` is never called. If `isLoaded()` throws an `Error`, `get()` catches it, calls `load()` once - that is where you navigate - and then calls `isLoaded()` again, this time outside any `try`. A second `Error` propagates out of `get()` and fails the test with whatever message your own check wrote. You implement two methods: `protected void load()` and `protected void isLoaded() throws Error`. Readiness therefore lives in the screen object instead of being repeated in every test.

code

java · 28 lines
java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.LoadableComponent;

public class HiveLogPage extends LoadableComponent<HiveLogPage> {

  private final WebDriver driver;

  public HiveLogPage(WebDriver driver) {
    this.driver = driver;
  }

  @Override
  protected void load() {
    driver.get("https://apiary.example/hives/17/log");
  }

  @Override
  protected void isLoaded() throws Error {
    if (driver.findElements(By.id("hive-log-table")).isEmpty()) {
      throw new AssertionError("Hive log table not rendered at " + driver.getCurrentUrl());
    }
  }

  public int inspectionRowCount() {
    return driver.findElements(By.cssSelector("#hive-log-table tbody tr")).size();
  }
}

go deeper

for a junior

Be ready to name the three steps in order: check, load once on failure, check again. Knowing that an already-open screen skips load() entirely is the detail most junior answers miss.

for a middle

Explain why the second isLoaded() is deliberately outside the try block, and what the caller receives when it fails. Be able to write the two abstract methods for a screen from memory.

for a senior

Show where this belongs in a real suite: which readiness condition is cheap enough to run on every call, and why a guard in the screen object beats the same check repeated at the top of thirty tests.

for a principal

Own the question of whether a codebase adopts this base class at all, given that only the Java and .NET bindings ship it and a polyglot suite would have to hand-write the equivalent elsewhere.

## The class you extend `LoadableComponent<T extends LoadableComponent<T>>` is an abstract class in the package `org.openqa.selenium.support.ui`, shipped in Selenium's Java support artifact `org.seleniumhq.selenium:selenium-support`. It models **anything that can be loaded** - a whole page, a widget inside one, even a service. - The type parameter is **self-referential**. A hive-log screen object declares `class HiveLogPage extends LoadableComponent<HiveLogPage>`, so `get()` returns `HiveLogPage` rather than a base type and the caller can chain straight into a method such as `inspectionRows()` with no cast. - The base class has exactly **one public method**, `get()`, and **two abstract methods** you must implement: `protected abstract void load()` and `protected abstract void isLoaded() throws Error`. - The base class never touches a browser. It does not import `WebDriver` at all - your subclass holds the driver, navigates, and does every lookup. ## What get() executes, in order 1. Call `isLoaded()`. 2. If it returns normally, cast `this` to `T` and return it. **`load()` is not called.** 3. If it throws an `Error`, catch it, discard it, and call `load()` **once**. 4. Call `isLoaded()` **a second time**, this time not wrapped in a `try`. 5. Return `this`. Step 4 is the half people forget. The re-check is **unguarded**: if the screen still is not ready, that second `Error` - carrying the message your own check wrote - travels out of `get()` and fails the test. There is no third attempt and no retry loop. ## A hive-log screen object ```java class HiveLogPage extends LoadableComponent<HiveLogPage> { private final WebDriver driver; HiveLogPage(WebDriver driver) { this.driver = driver; } @Override protected void load() { driver.get("https://apiary.example/hives/17/log"); } @Override protected void isLoaded() throws Error { if (driver.findElements(By.id("hive-log-table")).isEmpty()) { throw new AssertionError("Hive log table not rendered at " + driver.getCurrentUrl()); } } } ``` A test then writes `HiveLogPage log = new HiveLogPage(driver).get();` and knows that when the line returns, the hive-log table is on screen. ## Already loaded versus not loaded | Situation | First `isLoaded()` | `load()` | Second `isLoaded()` | Outcome | |---|---|---|---|---| | Browser already on the hive log | returns | not called | not called | `get()` returns the component at once | | Browser elsewhere, log renders | throws `Error` | called once | returns | `get()` returns the component | | Browser elsewhere, log never renders | throws `Error` | called once | throws `Error` | the `Error` leaves `get()` and fails the test | | `isLoaded()` throws a plain exception | throws exception | **not called** | not reached | the exception escapes; nothing is ever navigated to | The last row is the one that bites: `get()` catches `Error`, not `Throwable`. A readiness check that lets a `RuntimeException` out never reaches the load step at all. ## Why the check comes before the action - **Idempotence.** `new HiveLogPage(driver).get()` is safe to call from any helper. If the browser is already showing the hive log, the call costs one cheap DOM check and re-navigates nothing, so a half-typed inspection row is not thrown away by a redundant `driver.get(...)`. - **Readiness is written once.** The condition that says "this screen is usable" lives in one method next to the locators it depends on, instead of being copy-pasted into each test that opens the screen. - **Failures get a name.** The message on the `AssertionError` is authored by you, so a broken run reports "Hive log table not rendered at ..." rather than an anonymous lookup failure several lines later. - **`load()` stays a pure action.** Because the guard is elsewhere, `load()` can be the single navigation statement - no defensive checking, no "am I already here?" branch. ## What LoadableComponent is not - **Not a wait loop.** The base class does not poll and has no timeout. It performs one check, at most one load, and one re-check. A screen that finishes rendering after `load()` returns needs its sibling class `SlowLoadableComponent`, which adds a clock, a deadline and a poll interval. - **Not a driver factory.** It neither creates nor quits the `WebDriver`; the subclass is handed one. - **Not language-universal.** It is a Java support-library class with a .NET counterpart; Selenium's Python, Ruby and JavaScript bindings ship no equivalent, so in those languages the pattern is hand-written.

  • If load() navigates but the screen renders slowly, what does get() do?
    Nothing helpful. `LoadableComponent.get()` re-checks exactly once immediately after `load()` returns, so a screen that is still rendering fails that second `isLoaded()` and the `Error` escapes. That case is what `SlowLoadableComponent` exists for: it polls `isLoaded()` against a `java.time.Clock` until a `Duration` deadline before giving up.
  • Why is the class declared as LoadableComponent<T extends LoadableComponent<T>>?
    So `get()` can return the concrete subclass type. `HiveLogPage extends LoadableComponent<HiveLogPage>` makes `get()` return `HiveLogPage`, letting a test write `new HiveLogPage(driver).get().inspectionRowCount()` with no cast. The bound `T extends LoadableComponent<T>` is what stops a subclass naming an unrelated type as its return.
  • Can get() be called more than once on the same instance?
    Yes, and that is the point. Each call re-runs `isLoaded()`; if the screen is still open the call returns immediately without navigating. That makes it safe to start every test method or helper with `get()` as a cheap assertion of where the browser is.

It is the way you treat a front door: try the handle first, dig out the key only if it is locked, then try the handle once more to be sure you are actually inside.

saying these in an interview costs you the question

  • Says get() always calls load(), even when the screen is already open
  • Thinks isLoaded() returns a boolean that get() inspects
  • Believes get() keeps retrying load() until the screen appears
  • Expects get() to swallow the second failure and return anyway
  • Assumes LoadableComponent creates or owns the WebDriver instance