In Selenium, what does each driver.findElement call cost when a test loops over many rows?
answer
- A find is not a local operation
- Something crosses a process boundary each time
- One HTTP command per call
- The plural form brings them all back at once
- Three hundred rows means three hundred POSTs
basics
~20 sEach findElement call is one HTTP command to the browser driver, sent as POST /session/{id}/element. A loop over 300 rows sends 300 separate commands, and those round trips add up no matter how simple the selector is.
solid answer
~40 sA find is a remote command, not a local operation - there is no copy of the DOM in the test process. `driver.findElement(By.cssSelector("#reviewQueue tbody tr"))` sends `POST /session/{id}/element` to the remote end, which runs the match inside the page and returns one element reference. `driver.findElements` sends `POST /session/{id}/elements` and brings the whole matching set back in that same single response. A find scoped to an element you already hold is a further command, `POST /session/{id}/element/{elementId}/element`. So a loop that calls `findElement` once per row of a scholarship-application review queue costs one round trip per row, plus another for every value you then read with `getText`. Cutting the number of commands is the lever; changing selector syntax is not.
code
java · 12 linesWebDriver driver = new ChromeDriver();
driver.get("https://grants.example.org/review-queue");
List<WebElement> rows = driver.findElements(By.cssSelector("#reviewQueue tbody tr"));
List<String> applicants = new ArrayList<>();
for (WebElement row : rows) {
applicants.add(row.findElement(By.cssSelector("td.applicant")).getText());
}
System.out.println(applicants.size());
driver.quit();go deeper
Be ready to say that every findElement call travels to the driver as its own request and comes back with a reference. The count of calls, not the selector syntax, is what drives the cost.
Explain the endpoints: POST /session/{id}/element for a find, a separate scoped-find command against an element reference, and another command for each read such as getText. Then do the arithmetic for a loop over a table.
Demonstrate that you count commands before optimising anything. Show how you collapse an N-round-trip loop into one or two commands, and be honest about the readability you trade away when you do.
Frame the round trip as the unit of cost the whole harness is priced in, and set the expectation that selector-syntax rules will not move it. Decide where the team may trade clarity for command count.
## A find is a remote call, not a library call The most common junior misconception about Selenium is that `driver.findElement(...)` searches something the test process holds. It does not. There is no copy of the DOM in your JVM. Every find is a **command**: the client serialises the locator, sends an HTTP request to the **remote end** (the browser driver, or a Grid that forwards to one), waits for the response, and turns the returned **element reference** into a `WebElement` handle. The matching happens in the browser; the test process only asks and waits. That is why the cost of finding elements is counted in **commands**, not in characters of selector. Three consequences follow, and a junior is expected to name all three: - A locator is only data until it is sent. `By.cssSelector("td.applicant")` allocates a small local object and does nothing to the page on its own. - What comes back is an **element reference**, a handle the remote end recognises, not a copy of the node. Reading anything off it is another command. - If the browser is on another machine the call still costs exactly one command; it simply costs a longer one, because the request has further to travel. ## The four find endpoints | Java call | Command name | HTTP request | |---|---|---| | `driver.findElement(by)` | `findElement` | `POST /session/{id}/element` | | `driver.findElements(by)` | `findElements` | `POST /session/{id}/elements` | | `row.findElement(by)` | `findChildElement` | `POST /session/{id}/element/{elementId}/element` | | `row.findElements(by)` | `findChildElements` | `POST /session/{id}/element/{elementId}/elements` | The singular forms return one reference or fail; the plural forms return the whole matching set in a **single response**. That asymmetry is the whole optimisation story: one request can bring back three hundred references just as easily as one. ## The arithmetic of a loop Take a scholarship-application review queue with 300 rows, and a loop that reads each applicant's name: 1. `driver.findElements(By.cssSelector("#reviewQueue tbody tr"))` - **1 command**, 300 references back. 2. `row.findElement(By.cssSelector("td.applicant"))` inside the loop - **300 commands**, one scoped find per row. 3. `.getText()` on each result - **300 more commands**, because every `WebElement` method is its own request too. That is 601 round trips. Written the naive way, with a fresh `driver.findElement` per row instead of the plural call at step 1, it becomes 900. Nothing in that arithmetic changes if you rewrite every locator from `By.xpath` to `By.cssSelector`: the syntax only affects what happens inside each of those requests. ## What reduces the count and what does not - **Does reduce it:** replacing N singular finds with one plural `findElements`, so the set arrives in one response. - **Does reduce it:** not re-finding an element you already hold a reference to, as long as the page has not re-rendered it away. - **Does not reduce it:** shortening the selector string. A shorter body is a smaller payload, not a smaller number of requests. - **Does not reduce it:** switching locator strategy. `By.id`, `By.cssSelector` and `By.xpath` all cost exactly one command per call. - **Does not reduce it:** reusing the same `By` object across iterations. `By` instances are cheap local objects; the request is what costs. ## What one round trip is made of Each command carries fixed overhead independent of your selector: - JSON encoding in the client and decoding in the driver, then the same again on the way back. - One HTTP request and response over a socket. On loopback that is small; against a remote end reached over a network it is larger, and every command pays it. - The driver's own bookkeeping: resolving the session, checking the browsing context is still open, handling any open user prompt, and minting or looking up element references. None of that scales with how clever your selector is. It scales with **how many times you ask**. ## The habit this should build When a Selenium test that touches a long list feels slow, the first question is not "should this be CSS?" - it is "how many commands does this method issue?" Count them from the code before you profile anything: one per find, one per scoped find, one per read such as `getText`, `getDomAttribute` or `isDisplayed`, one per interaction. On a 300-row review queue, a method that reads three fields per row is issuing well over a thousand requests, and that number is the only one worth attacking. The corollary matters for design too: a plural find that returns references you then use is usually the cheapest shape available in the API, and it costs the same one command whether it matches one element or five hundred.
- How many commands does row.findElement(By.cssSelector("td.applicant")).getText() send?Two. The scoped find is `POST /session/{id}/element/{elementId}/element` and returns a new element reference, then reading the text is a further command against that new reference. Every WebElement method you chain after a find is another request on the wire.
- Does findElements cost more than findElement when many elements match?Only marginally. Both are one command; the plural form serialises every match into the response instead of one, so the payload grows with the match count. That is far cheaper than issuing one find per match, which is the alternative it replaces.
- If the driver runs on the same machine as the test, is the round trip still worth worrying about?Yes, at volume. A loopback request plus JSON encoding and decoding on both sides is small individually, but a suite issuing tens of thousands of finds pays it tens of thousands of times. Against a remote end reached over a network the same arithmetic gets worse.
saying these in an interview costs you the question
- Thinks findElement searches a local copy of the DOM in the test process
- Believes a simpler selector reduces the number of commands sent
- Assumes findElements is slower than findElement because it returns more
- Counts only the find calls and forgets each getText is another command
- Says holding a WebElement avoids all further round trips to the browser