skip to content

In Selenium 4, what breaks when a Java wait helper for a report-card page is hand-ported to Python?

level: seniorimportance: must knowfreq 48%

answer

  1. Same commands, five different spellings
  2. Count the arguments the find call takes
  3. Check what type the timeout wants
  4. Java holds a By object, Python holds a pair
  5. Duration, TimeSpan, seconds, milliseconds

basics

~20 s

Four surfaces change: method names go snake_case, the single By object becomes two arguments (a tuple inside condition helpers), the Duration timeout becomes a bare number of seconds, and text is read as a property.

solid answer

~30 s

The commands are the same; the spelling is not. `driver.findElement(By.id("term-average"))` becomes `driver.find_element(By.ID, "term-average")` - two arguments, because Python's `By.ID` is a strategy constant rather than a factory. Inside `expected_conditions` the same pair must be a tuple: `visibility_of_element_located((By.ID, "term-average"))`. In Selenium 4 the Java wait takes a `Duration`, so `Duration.ofSeconds(10)` becomes a plain `10` seconds in Python, and reads become properties (`element.text`, not `getText()`). The port that actually bites is to JavaScript, where `driver.wait(cond, 10)` is ten milliseconds, not ten seconds.

code

java · 21 lines
java
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class ReportCardWait {
    public static void main(String[] args) {
        ChromeDriver driver = new ChromeDriver();
        try {
            driver.get("https://school.example/report-cards");
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
            WebElement average = wait.until(
                    ExpectedConditions.visibilityOfElementLocated(By.id("term-average")));
            System.out.println(average.getText());
        } finally {
            driver.quit();
        }
    }
}

go deeper

for a junior

Be ready to name the everyday spellings side by side: findElement against find_element, By.id against By.ID, getText against text. Recognising them is enough at this stage.

for a middle

Explain why the shapes differ - a locator object in one binding, a strategy constant plus a value in another - and name the timeout type each binding's wait constructor demands.

for a senior

Show that you would review a cross-language port for the silent failures: a millisecond timeout read as seconds, a missing await, a condition handed to a wait that wanted a block. Say how you would catch each one.

for a principal

Own the question of whether a suite should span two bindings at all, what that costs in duplicated helpers and review load, and what has to be shared - selector strings, page contracts - if it does.

## What a port actually has to change A Selenium test is a sequence of commands the client library sends to the browser's driver, and the five official clients - **Java**, **Python**, **C#**, **Ruby** and **JavaScript** - express those same commands in their own house style. Porting the report-card suite's wait helper from Java to Python is therefore not a redesign; it is a mechanical rewrite of four surfaces, and nearly every classic porting bug lives in one of them. 1. **Method naming** - `findElement` becomes `find_element`. 2. **The locator argument** - one `By` object becomes two arguments, or a tuple. 3. **The timeout type** - a `Duration` becomes a bare number, and the unit is not the same everywhere. 4. **How a value is obtained** - returned directly, read as a property, or awaited. ## Naming conventions, binding by binding Java's `driver.findElement(...)` is `driver.find_element(...)` in Python and Ruby, `driver.FindElement(...)` in C#, and `await driver.findElement(...)` in JavaScript, which keeps Java's camelCase. The readers diverge further: - **Java** and **JavaScript** use `get`-prefixed methods: `getText()`, `getTitle()`, `getCurrentUrl()`. - **Python** exposes most reads as properties: `element.text`, `driver.title`, `driver.current_url`. - **Ruby** drops the prefix (`element.text`, `driver.title`) and marks booleans with a question mark: `displayed?`, `enabled?`, `selected?`. - **C#** uses PascalCase and turns the same reads into properties: `element.Text`, `element.Displayed`, `driver.Url`. ## The locator argument is the first real trap In Java a locator is a single object: `By.id("term-average")` is a value you can hold in a field and hand to anything that takes a locator. Python splits the same information into two arguments - `driver.find_element(By.ID, "term-average")` - where `By.ID` is a constant naming the lookup strategy. Ruby collapses both halves into one keyword argument, `driver.find_element(id: "term-average")`, and JavaScript mirrors Java's factory style with `By.id('term-average')` (note `By.css` there where Java writes `By.cssSelector`). The Python split has a consequence that catches every first port: the condition helpers in `selenium.webdriver.support.expected_conditions` take **one** locator argument, so the pair has to be wrapped in a tuple - `visibility_of_element_located((By.ID, "term-average"))`. The same two values are two arguments in one call and one tuple in the next. ## Timeouts: four different types for the same ten seconds | Binding | Explicit wait | Timeout argument | |---|---|---| | Java | `new WebDriverWait(driver, Duration.ofSeconds(10))` | a `java.time.Duration` | | Python | `WebDriverWait(driver, 10)` | a number of **seconds** | | C# | `new WebDriverWait(driver, TimeSpan.FromSeconds(10))` | a `TimeSpan` | | Ruby | `Selenium::WebDriver::Wait.new(timeout: 10)` | **seconds** as a keyword | | JavaScript | `driver.wait(condition, 10000)` | **milliseconds** | In **Selenium 4** the Java constructor that took a plain number of seconds is gone, so `Duration` is the only spelling there; C# likewise takes a `TimeSpan` rather than a number. The line that hurts is the JavaScript one: porting `10` verbatim gives you a ten-**millisecond** timeout that expires before the page can possibly settle, and the failure reads like a broken locator rather than a unit mistake. ## Where a mechanical rename still compiles The dangerous ports are the ones a compiler or interpreter accepts: - A JavaScript `driver.wait(cond, 10)` runs; it just never finds anything. - A forgotten `await` in JavaScript leaves you holding a promise object, and every promise is truthy, so `if (element.isDisplayed())` takes the true branch whether the report card is on screen or not. - Ruby's `Wait#until` expects a **block**, so handing it a condition object the way Java hands `ExpectedConditions...` to `until` leaves the call with nothing to evaluate and it fails immediately. - Python's `find_element_by_id` style helpers were **removed in Selenium 4.3**, so a port copied from an old sample raises `AttributeError` rather than doing anything subtle. ## A porting checklist 1. Rewrite the method names into the target language's casing, and turn `get`-prefixed readers into whatever that binding uses - a property, a bare method, or a `?` predicate. 2. Rewrite every locator into the target shape: one object, two arguments, or a keyword. 3. Convert the timeout to the target type **and unit**, checking milliseconds versus seconds explicitly. 4. Rewrite the condition: a static factory in Java, a module function in Python, a lambda in C#, a block in Ruby, a factory from the `until` module in JavaScript. 5. Keep the raw selector string identical across the halves of the suite - `term-average` is the only part of the locator that is genuinely portable, and it is the part worth centralising.

  • Which part of the port is genuinely not a spelling change?
    Very little at this layer. The command sequence and the selector strings survive untouched; what changes is naming, the locator's argument shape, the timeout's type and unit, and JavaScript's need to await a result. If a port changes what the test proves, the port is wrong.
  • How would you keep one locator definition usable by both halves of the suite?
    Share the raw selector string, not the locator object. In Java hold `private static final By TERM_AVERAGE = By.id("term-average")`; in Python hold `TERM_AVERAGE = (By.ID, "term-average")` and unpack it with `driver.find_element(*TERM_AVERAGE)`. The string `term-average` is the portable part.
  • A C# helper reads driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5). What is the Java line?
    `driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5))`. C# exposes the timeout as a settable property taking a `TimeSpan`, Java as a method taking a `Duration`, Python as `driver.implicitly_wait(5)` in seconds, and Ruby as `driver.manage.timeouts.implicit_wait = 5`.

saying these in an interview costs you the question

  • Says Python's find_element takes a By object like Java's
  • Passes 10 to the JavaScript wait expecting ten seconds
  • Assumes a behaviour difference where only the spelling differs
  • Hands By.ID and the value to expected_conditions as two arguments
  • Believes Selenium 4 Java still accepts a plain seconds number