In Selenium, why can a FluentWait with pollingEvery(Duration.ofSeconds(2)) check its condition less often than every two seconds?
answer
- A rest between laps, not a metronome
- The condition is not free to evaluate
- Sleep happens after the attempt finishes
- Effective gap equals evaluation time plus interval
- Every findElement is a driver round trip
basics
~10 sBecause pollingEvery sets a sleep between attempts, not a fixed cadence. The time the condition itself takes is added on top, so the real gap is the evaluation time plus the interval.
solid answer
~50 s`pollingEvery(Duration)` on a Selenium `FluentWait` schedules a **sleep after an unsuccessful attempt**, not a tick on a timer. The loop is: evaluate the condition, return if the result is neither null nor false, throw `TimeoutException` if the deadline has passed, otherwise sleep for the interval and repeat. Whatever the condition costs — every `findElement` is a round trip to the driver, and a remote Grid adds network latency to each one — is spent on top of the interval, so the effective period is evaluation time plus interval. Selenium's own documentation on the method says the interval may in reality be greater because the cost of evaluating the condition is not factored in. A condition costing 900 ms with a two-second interval really runs about every 2.9 seconds, so a twenty-second timeout buys around seven attempts rather than ten.
go deeper
Know that the polling interval is a sleep between checks, and that the wait is a plain loop on your own thread rather than something running in the background.
Explain the four steps of the loop and why the real gap is evaluation time plus interval. An interviewer at this level wants to hear that a remote round trip inside the condition is what makes the difference visible.
Demonstrate that you have measured this: knowing a wait can overrun its timeout by a whole evaluation, and that the failure text reports configured rather than observed numbers, is what lets you read a slow suite correctly.
Own the systemic version of the question: how much driver traffic a suite's polling generates across parallel sessions, and whether cheap conditions or fewer waits is the lever your team should pull.
## What pollingEvery actually sets `pollingEvery(Duration)` on Selenium's `FluentWait` sets **how long the thread sleeps after an attempt that did not succeed**. It is not a scheduler, not a metronome and not a background timer. The loop inside `until()` is deliberately simple: 1. apply the function to the input — for browser waits, the `WebDriver`; 2. if the result is neither `null` nor `Boolean.FALSE`, return it and stop; 3. if the deadline has passed, throw `TimeoutException`; 4. otherwise sleep for the configured interval, then go back to step 1. The sleep in step 4 is the only thing the interval controls. Whatever the function itself costs is spent **on top of** it. Selenium's own documentation on `pollingEvery` says exactly that: in reality the interval may be greater, because the cost of evaluating the condition function is not factored in. ## Why the real gap is bigger than the number you passed The effective period between attempts is roughly **evaluation time + interval**, and evaluation is rarely free: - every `findElement` inside the condition is a **round trip to the remote end** — a real HTTP request to the driver and back; - the expensive case is a condition that has to *fail* to find something, because the remote end does the full lookup before answering; - an `executeScript` call that asks the page whether the quote has settled is another round trip, plus however long the script runs; - a remote Grid puts network latency on every one of those round trips; - a condition that touches several elements pays the round trip once per element. Concretely, on a rooftop-solar quote builder whose condition reads the panel count and the annual-yield figure and compares them, an attempt might cost 900 ms. With `pollingEvery(Duration.ofSeconds(2))` you get an attempt roughly every 2.9 seconds, so a 20-second timeout buys about seven attempts, not ten. | | What you configure | What actually happens | |---|---|---| | Gap between attempt starts | the interval you passed | interval **plus** evaluation time | | Attempts inside a timeout | timeout / interval | fewer, and not a round number | | Cost of a slow condition | invisible in the config | charged once per attempt | | Reported in the failure text | the configured interval | the configured interval, never the observed one | ## The deadline is checked after an attempt, never during one The order in step 2 to step 3 above matters. `FluentWait` evaluates first and compares the clock afterwards, and the source carries a comment saying why: so that a condition given a **zero timeout can still succeed**. Two consequences follow: - The condition is always evaluated **at least once**, however small the timeout. - The wait can **overrun** its timeout by up to one evaluation. A condition that takes three seconds, started at 9.5 seconds into a 10-second wait, still runs to completion; `until()` returns or throws at about 12.5 seconds. There is no cancellation. `until()` never interrupts an attempt that is already running, so a condition that blocks — on a page-load timeout, say, or a script that never returns — blocks the whole wait past its deadline. The only interruption `FluentWait` handles is an interrupt of the **sleep**, and it responds by restoring the thread's interrupt flag and throwing `WebDriverException`. ## What the failure message tells you, and what it does not The `TimeoutException` text ends with `(tried for 20 seconds with 2000 milliseconds interval)`. Both of those numbers are the values you **configured**. Nothing in the message reports how many attempts actually ran or how long each one took, so drift between the configured cadence and the real one is invisible in the failure alone. If you need it, time the condition yourself or read the driver's own log of requests. ## Where the sleeping and the clock come from `FluentWait` does not call `Thread.sleep` directly. It holds a `Sleeper` and a `java.time.Clock`, both supplied by the constructor: - the one-argument constructor uses `Clock.systemDefaultZone()` and `Sleeper.SYSTEM_SLEEPER`, which is a lambda over `Thread.sleep`; - the three-argument constructor `FluentWait(T input, Clock clock, Sleeper sleeper)` lets a unit test inject a fake clock and a no-op sleeper, which is how Selenium tests its own wait without spending real seconds. That is also the seam to reach for when you want to prove a wait's behaviour in a unit test rather than against a live browser.
- How many times does a FluentWait evaluate its condition if the timeout is zero?Exactly once. `until()` applies the function first and only then compares the clock against the deadline, and the source carries a comment saying the order exists so that a condition with a zero timeout can still succeed. A zero-timeout wait is therefore a single check, not a no-op.
- Can a FluentWait return later than its configured timeout?Yes. The deadline is checked between attempts, never during one, and `until()` never interrupts a running condition. A condition that takes three seconds and starts at the nine-second mark of a ten-second wait runs to completion, so the wait returns or throws at about twelve seconds.
- Does the TimeoutException message tell you how many attempts actually ran?No. The text ends with a tried-for clause reporting the configured timeout and the configured interval, both of them the values you passed. It reports neither the number of attempts nor their real cost, so drift between the configured and observed cadence is invisible in the failure alone.
The interval is the rest a runner takes between laps, not the time a stopwatch allows per lap: the lap itself still takes as long as it takes, so laps come further apart than the rest alone suggests.
saying these in an interview costs you the question
- Thinks polling attempts start exactly one interval apart
- Assumes timeout divided by interval gives the attempt count
- Believes a background thread samples the condition
- Says the interval caps how long one attempt may run
- Thinks a running condition is cancelled at the deadline