skip to content

Custom Wait Functions

Writing the condition yourself as a lambda or a typed condition object: return null or false to keep polling, anything else to stop. Interviewers watch for side effects inside the loop.

on this pageshow

explore

questions

5

In Selenium, what shape must a custom condition passed to WebDriverWait.until have?

level: juniorimportance: should knowfreq 64%

answer

  1. One argument in, one value out
  2. The argument is the driver itself
  3. A plain Java lambda is enough
  4. Null means not ready, poll again
  5. Return what the next line needs

basics

~20 s

A function from WebDriver to some value. The wait passes the driver in on every poll, and the first result that is neither null nor false ends the wait and becomes the return value of until.

solid answer

~40 s

It is a function taking the `WebDriver` and returning a value: `until` is declared as `until(Function<? super WebDriver, ? extends V>)`, so in Selenium 4 a plain Java lambda such as `d -> ...` is enough, and `ExpectedCondition<T>` is the named alternative that implements the same `java.util.function.Function<WebDriver, T>`. The wait applies it repeatedly, handing in the driver each time, and the first result that is neither `null` nor `false` ends the loop and becomes the value of `until`. Return `null` for the not-ready case. Prefer returning the thing you need next rather than a boolean: on a plant-nursery order form, a condition that returns the 'Place order' button once `isEnabled()` is true saves you finding it again on the following line.

code

java · 8 lines
java
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));

WebElement placeOrder = wait.until(d -> {
  WebElement button = d.findElement(By.id("place-order"));
  return button.isEnabled() ? button : null;
});

placeOrder.click();

go deeper

for a junior

Memorise the signature: driver in, value out, null means keep waiting. Practise writing one lambda that returns an element only once it is enabled, then acting on the returned value.

for a middle

Explain why the return type of the lambda becomes the return type of until, and when a named condition earns its keep over an inline lambda in a shared suite.

for a senior

Show judgment about failure messages and reuse: which checks deserve a named condition with a readable toString, and how that shortens diagnosis when a wait times out in CI.

for a principal

Set the convention. Decide whether custom conditions live inline, in a shared library, or behind factory methods, and what that choice costs in readability and duplication across a large suite.

## The signature `until` accepts A custom wait is just a function you hand to `until`. In Selenium 4 the `Wait` interface declares it as `<V> V until(Function<? super F, ? extends V> isTrue)`, where `F` is the input type — `WebDriver` for a `WebDriverWait`. Two consequences follow: - The function's **argument** is the driver the wait was constructed with. The wait passes it in on every evaluation, which is what lets the body look at the live page each time. - The function's **return type** becomes the return type of `until`. Whatever your last poll produced is what the call gives back. That `Function` is `java.util.function.Function`, so an ordinary Java lambda is enough — no library type is required. `ExpectedCondition<T>` still exists as a named alternative; it extends `java.util.function.Function<WebDriver, T>` (and, for source compatibility with old code, Guava's `Function` as well), so anywhere one is accepted the other works too. ## Lambda or named condition | | Lambda | `ExpectedCondition<T>` class | |---|---|---| | Where it lives | inline at the call site | its own class or a factory method | | Reuse | none, it is one expression | shared across tests | | Timeout message | shows the synthetic lambda name | shows whatever `toString()` you write | | Best for | a one-off check in a single test | a check the suite repeats everywhere | The timeout-message row is the practical reason to reach for a named condition. When a wait fails, the message includes the condition's `toString()`; a lambda has a machine-generated one, so the failure reads as a class name with digits in it. A named condition that overrides `toString()` to return something like `"subtotal on the nursery order to be priced"` makes the failure self-explaining. ## What comes back out of `until` The loop returns the **first result that is neither `null` nor a `false` `Boolean`** — so design your condition to return the thing you want next, not merely a yes/no: 1. Decide what the following line of the test needs: an element to click, a string to assert on, a list to count. 2. Make the condition produce exactly that when the page is ready. 3. Return `null` while it is not ready, so the loop keeps going. 4. Assign the result of `until` and use it directly, instead of finding the same thing again on the next line. Returning `Boolean` is fine when there is nothing worth carrying out — a flag that a panel has disappeared, for instance — but returning the element saves a second lookup and removes a window in which the page can change between the wait and the action. ## A worked nursery example Waiting for the "Place order" button on a **plant-nursery order form** to become enabled once the delivery week is chosen: ```java WebElement placeOrder = new WebDriverWait(driver, Duration.ofSeconds(15)) .until(d -> { WebElement button = d.findElement(By.id("place-order")); return button.isEnabled() ? button : null; }); placeOrder.click(); ``` Read it against the signature: the parameter `d` is the driver, the body performs reads only, `null` means "not yet", the button itself means "ready", and `placeOrder` is the value the successful poll produced. ## Common shape mistakes - **Returning nothing.** A lambda whose body is a statement returns `void`, which does not compile against `until`; the wait's contract requires a value. - **Returning a boolean expression when you wanted the element**, then finding the element again on the next line and re-introducing the race you just waited out. - **Doing the action inside the lambda** instead of returning a value for the caller to act on. - **Capturing an element from outside the lambda** rather than finding it inside, so the body reads a handle that may no longer resolve. - **Assuming a `false` return is an error.** It is not: `false` and `null` both simply mean "not ready, poll again", and the wait only fails when the deadline passes. A useful habit while learning: read every custom wait you meet by naming its three parts out loud - what goes in, what the body reads, and what each branch returns. Conditions that are hard to describe that way are usually doing something other than observing readiness, and that is worth noticing before the wait reaches CI. If you can state the signature — a function from `WebDriver` to some value, called repeatedly, whose non-null non-false result becomes the answer — every other rule about custom waits follows from it.

  • When would you write a named ExpectedCondition class instead of an inline lambda?
    When the check is reused across tests, or when the failure message matters. The timeout message includes the condition's `toString()`, and a lambda's is machine-generated, so a shared check reads better as a named class or factory method that overrides `toString()` with a description of what it was waiting for.
  • What happens if the condition body performs a statement and returns nothing?
    It does not compile. `until` is declared to return a value and requires a function that produces one, so a void body has no matching type. That constraint is deliberate: a condition with no result gives the wait nothing to test, and the interface documents that the return type must not be void.
  • Does returning the element rather than a boolean change how the wait behaves?
    Not the polling behaviour, only what you get back. Both keep polling until they stop being null or false. Returning the element means the value of `until` is directly usable, which removes the second lookup and the small window between the wait finishing and you finding the element again.

saying these in an interview costs you the question

  • Thinks the condition must return a boolean to be valid
  • Expects until to pass the element rather than the driver
  • Writes a void lambda body and expects it to compile
  • Believes a false return is an error rather than not-ready
  • Ignores the value until returns and re-finds the element
open as a page

In a Selenium custom wait condition, how do you poll a page state that no element reflects?

level: juniorimportance: should knowfreq 41%

basics

~20 s

Cast the driver the wait hands you to JavascriptExecutor and return the result of executeScript from the condition. The fragment needs an explicit return statement, or every poll receives null and the wait just times out.

open as a page

In Selenium's Java WebDriverWait, which values returned by a custom condition keep it polling?

level: middleimportance: should knowfreq 58%

basics

~20 s

Only two: null, and a Boolean whose value is false. Every other result ends the wait and is returned by until, including an empty list, an empty string and a zero, which is why an empty findElements result never waits.

open as a page

Why must a custom condition passed to Selenium's WebDriverWait.until be free of side effects?

level: seniorimportance: should knowfreq 52%

basics

~20 s

The condition is re-evaluated on every poll until it succeeds or times out, so any action inside it runs many times over. Selenium documents conditions as idempotent: keep the body to reads and act after until returns.

open as a page

Your custom Selenium wait condition reuses a WebElement found before the wait and dies with StaleElementReferenceException. Why?

level: seniorimportance: should knowfreq 47%

basics

~20 s

The lambda captured an element handle taken before the page re-rendered, so a later poll touches a detached node. WebDriverWait ignores only NotFoundException by default, and staleness is not one, so it aborts the wait.

open as a page