skip to content

Your Selenium page classes use Java's @FindBy; what must change when the suite moves to the Python client?

level: seniorimportance: should knowfreq 40%

answer

  1. One binding ships that support package
  2. There is nothing to translate into
  3. Locator strings cross languages for free
  4. Fields become constants plus accessors
  5. Watch where the lookup now happens

basics

~20 s

The annotated fields have no equivalent, so they become explicit find calls. Locator strings, locator strategies and the page structure port unchanged; the real risk is resolving elements in the constructor and changing when the lookup happens.

solid answer

~40 s

`@FindBy` and `PageFactory` live in the Java binding's `org.openqa.selenium.support` package, and at Selenium 4 no other official client ships an equivalent, so there is nothing to translate those fields into. Each annotated field becomes a locator constant plus a small accessor method that performs the find when it is called, and the initialisation call disappears. What survives untouched is everything protocol-level: the CSS or XPath strings, the standard locator strategies, the one-class-per-screen structure and the assertions. The trap is timing. The Java fields were resolved at first use, not at construction, so a port that eagerly finds every element in the new page class's constructor changes behaviour and will break on any screen that renders late or re-renders.

code

python · 19 lines
python
from selenium.webdriver.common.by import By


class AppointmentBookPage:
    GRID = (By.ID, "appointment-grid")
    FREE_SLOT = (By.CSS_SELECTOR, "#appointment-grid .slot--free")
    CONFIRM = (By.ID, "confirm-appointment")

    def __init__(self, driver):
        self.driver = driver

    def grid(self):
        return self.driver.find_element(*self.GRID)

    def free_slots(self):
        return self.driver.find_elements(*self.FREE_SLOT)

    def confirm_button(self):
        return self.driver.find_element(*self.CONFIRM)

go deeper

for a junior

Know that annotated page fields are a Java-binding feature, not a Selenium feature, and that other clients find elements with explicit calls. Recognising the name PageFactory is enough here.

for a middle

Explain the mechanical port: each annotated field becomes a locator constant and an accessor, the initialisation call goes away, and locator strings stay exactly as they were because the browser evaluates them.

for a senior

Demonstrate the timing judgment. Point out that the annotated fields resolved at first use, and that an eager constructor in the ported class silently changes behaviour on late-rendering or re-rendering screens.

for a principal

Own the estate-level call: whether the team recreates a binder for familiarity or accepts idiomatic differences between language suites, and what that choice costs in onboarding and long-term maintenance.

## Where @FindBy actually lives `@FindBy` and `PageFactory` are not part of the WebDriver protocol and are not part of every Selenium client. They live in the **Java** binding's `org.openqa.selenium.support` package, and at **Selenium 4** the Java client is the only official binding that ships them. The Python, C#, Ruby and JavaScript clients have no annotation-driven or attribute-driven field binder; a page class written in those languages calls the find methods explicitly. So the port is not a translation exercise for those fields. There is nothing to translate them *into*. ## What ports unchanged The good news is that most of the page class is protocol-level and survives the move intact: - **The locator strings.** `#appointment-grid`, `button#confirm-appointment` or an XPath over the vet column are evaluated by the browser, not by the client, so they behave identically. - **The locator strategies.** `id`, `css selector`, `xpath`, `link text`, `partial link text`, `tag name` and `name` are the standard's own strategies; every client can express all of them. - **The page's structure.** The idea of one class per screen of the appointment book, with one method per user action, is a design choice in your code, not a Selenium feature. - **The assertions.** Whatever the test claims about the booked slot is your code and your language's own tooling. ## What has no counterpart | Java page class element | In the other official clients | |---|---| | `@FindBy(id = "appointment-grid")` on a field | no equivalent, write the find call in a method | | `PageFactory.initElements(driver, this)` | nothing to call, there is no binder | | A field typed as an element, resolved later | you hold the locator and find on demand | | The annotation's `how` and `using` pair | expressed as a locator argument at the call site | ## Rewriting an appointment-book page The mechanical shape of the port is small and repetitive: 1. **Turn each annotated field into a locator constant** — the strategy plus the string, kept together so the page still has one place to change when the markup changes. 2. **Turn each field read into a method** that performs the find at the moment it is called. 3. **Delete the initialisation call** that used to bind the fields, along with the import of the support package. 4. **Re-test the timing-sensitive screens**, because the moment of lookup has moved. ## The timing change hidden in the port This is the part that surprises teams, and the part an interviewer is usually probing. In the Java version, a field declared with an annotation is not looked up when the page object is constructed; the lookup happens when the field is used. An explicit port reproduces that only if you also find the element inside the method rather than in the constructor. If you "helpfully" resolve every element once in the constructor of the new Python page class, you have changed behaviour: the appointment book's confirm button is now looked up before the day view has finished rendering, and elements captured before a re-render are no longer valid afterwards. Keeping the find inside the accessor method — as in the example accompanying this answer — preserves the original timing without needing a binder at all. ## How to answer the "should we just build one?" follow-up Teams sometimes propose recreating the annotation binder in the target language so the two suites look alike. That is a real option, and the honest trade-off is worth stating: you gain visual similarity between two codebases that are already different languages, and you take on a piece of reflective infrastructure that every new engineer must learn and that the Selenium project will never document for you. The cheaper answer for most veterinary-appointment-book suites is a locator constant plus a one-line accessor, which is idiomatic in every one of the five official clients and needs no explanation at all. The port is therefore best described as: **locators and page structure move for free, the binder does not move at all, and the only real risk is accidentally changing when the lookup happens.**

  • Why not resolve every element once in the new page class's constructor and store them?
    Because it changes when the lookup happens. The annotated Java fields were resolved at first use, so a page object could be constructed before the screen finished rendering. Resolving eagerly means every element must already exist at construction, and any stored reference is invalidated by a re-render of the appointment grid.
  • Would you rebuild an annotation-style binder in the target language so both suites look alike?
    Rarely. You would gain cosmetic similarity between two codebases already written in different languages, and take on reflective infrastructure the Selenium project will never document for you. A locator constant plus a one-line accessor is idiomatic in every official client and needs no explanation.
  • What parts of the Java suite are safe to copy across verbatim?
    The locator strings themselves, because the browser evaluates them, and the standard strategies behind them. The page's decomposition into screens and user actions is your own design and carries over as a plan, though every line of it gets rewritten in the new language.

saying these in an interview costs you the question

  • Expects an equivalent annotation in the Python client
  • Says the whole page object must be redesigned from scratch
  • Rewrites locator strings when changing language
  • Resolves every element in the constructor after the port
  • Believes PageFactory is part of the WebDriver protocol