Skip to content
Testing

11 QA Selenium and Playwright Interview Questions and Answers

This focused guide turns RecallDeck’s curated QA Selenium and Playwright material into 11 interview-ready questions. Answer each one before opening the explanation, then use the examples and edge cases to repair anything vague or incomplete.

10 min read11 detailed answersReviewed Aug 24, 2026
What to remember

Describe the risk first, then choose the smallest test layer that provides useful evidence. A tool name without an oracle or failure model is not a test strategy.

Question set

11 detailed answers

01

What locators does Selenium support, and when is XPath better than CSS?

Short answer: Selenium supports eight strategies: id, name, className, tagName, linkText, partialLinkText, cssSelector and xpath. CSS is faster and more readable, but XPath can do what CSS cannot: traverse up and backwards through the DOM (ancestor, parent, preceding-sibling) and locate an element by its text. That asymmetry is the whole point.

In depth:

  1. CSS selector — the default choice: terse syntax, runs faster (the browser optimizes its CSS engine), covers 90% of cases.
  2. XPath upward navigation — only XPath can go from a found element to its parent/ancestor: //span[text()='Total']/ancestor::tr.
  3. XPath by text//button[text()='Pay'] or contains(text(),'...'); CSS has no way to match on text content at all.
Capability CSS XPath
Speed higher lower
Readability higher lower
Up the DOM no yes
Match by text no yes
Element index :nth-child [2]

⚠️ Common mistake: not knowing the asymmetry and answering "XPath is more powerful." It's only more powerful for upward and text-based navigation — for everything else CSS is the default.

02

How do implicit and explicit waits differ, and why shouldn't you mix them?

Short answer: An implicit wait is a global element-search timeout for the whole driver (default 0, i.e. off): the driver waits up to that time for any element to appear. An explicit wait is a WebDriverWait for a specific condition on a specific element, polling the state roughly every 500 ms. You can't mix them: the timeouts stack unpredictably.

In depth:

  1. Implicit — set once (driver.implicitly_wait(10)), applies to every find_element; waits only for the element's presence in the DOM, not for visibility or clickability.
  2. ExplicitWebDriverWait(driver, 10).until(EC.element_to_be_clickable(...)); waits for the desired state, default poll interval 500 ms.
  3. Why not mix — with both enabled the driver can compound the waits in unpredictable ways, and instead of 10 seconds the test waits minutes.
Property Implicit Explicit
Scope whole driver single call
Default 0 (off) none
Condition presence in DOM any (EC)
Poll ~500 ms

⚠️ Common mistake: enabling implicitly_wait and layering WebDriverWait on top. The rule is to pick one mechanism, usually explicit, and keep implicit at 0.

03

Why is Thread.sleep() an anti-pattern in automated tests, and what replaces it?

Short answer: Thread.sleep() is a static pause for a fixed duration. On a fast environment it slows the run for nothing (waiting 5 seconds where 200 ms would do), and on a slow one it's still not enough and the test flakes. The replacement is conditional waits that poll the state and continue the instant the condition is met: WebDriverWait + ExpectedConditions in Selenium or Playwright's built-in auto-wait.

In depth:

  1. The problem — sleep knows nothing about page state; it's always either too long or too short.
  2. The fix — a condition-based wait: until(EC.visibility_of_element_located(...)) finishes exactly when the element is ready.
# Bad: a blind pause
time.sleep(5)
driver.find_element(By.ID, "submit").click()

# Good: wait for the exact state you need
WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "submit"))
).click()

⚠️ Common mistake: "the test flakes — I'll bump the sleep." That treats the symptom at the cost of speed and still guarantees nothing; the right answer is an explicit wait for a condition.

04

A test fails one run out of five. How do you fix it?

Short answer: You fix a flaky test by systematically finding the root cause, not by retrying. I run it in isolation N times, capture logs, a screenshot and a DOM snapshot on failure, classify the cause and fix the root. Retries mask instability rather than solve it — say that out loud.

In depth:

  1. Reproduce — run the test in isolation 20–50 times, catch the failure, record its frequency.
  2. Gather evidence — on failure capture a screenshot, an HTML dump of the DOM, browser logs and a trace.
  3. Classify the cause — usually one of five:
Cause Symptom Fix
Waits (race) element hadn't appeared yet explicit wait on a condition
Brittle locator sometimes not found stable data-testid
Test data depends on others' data isolation/fixtures
Test order fails only in the suite remove shared state
Environment fails only in CI resources, timing, network
  1. Fix the root and verify — run N times again, get a stable green.

⚠️ Common mistake: wrapping the test in @Retry(3) and closing the ticket. That hides the bug — sometimes a real product defect — and piles technical debt into the suite.

05

What exceptions does Selenium throw, and what does StaleElementReferenceException mean?

Short answer: The most common exceptions are NoSuchElementException, StaleElementReferenceException, TimeoutException and ElementNotInteractableException, each tied to its own cause. StaleElementReferenceException means the element reference is stale: the DOM re-rendered and the old WebElement no longer points to a live node. The fix is to re-fetch the element, not to wrap everything in try/except.

In depth:

Exception Cause Fix
NoSuchElement not in DOM / wrong locator wait for presence, check selector
StaleElementReference DOM re-rendered, reference expired find the element again
Timeout WebDriverWait condition never met raise the timeout or fix the condition
ElementNotInteractable present but hidden/covered/disabled wait for clickable, scroll

Stale-element practice: re-read the element right before acting on it instead of holding an old reference across a re-render (typical in React/SPA after a state update).

⚠️ Common mistake: swallowing StaleElementReferenceException with a try/except retry loop. That hides the real race with the re-render — the correct fix is to re-query the element once the DOM has settled.

06

What is JavaScriptExecutor for, and when is using it a red flag?

Short answer: JavaScriptExecutor runs arbitrary JS right in the browser when the plain WebDriver API isn't enough: scrolling to an element, clicking a covered element, reaching into the shadow DOM, reading a JS variable off the page. But clicking via JS bypasses the real user path — if a test "only works through JS," that's a red flag: you may have hit a UI bug.

In depth:

  1. Legitimate cases — scrolling (scrollIntoView), reading state (return document.title), interacting with elements WebDriver physically can't reach (shadow DOM, tricky overlays).
  2. Red flagarguments[0].click() instead of a normal click: WebDriver refused to click because the element is invisible/covered/disabled — meaning a real user couldn't click it either.
// Scroll — fine
js.executeScript("arguments[0].scrollIntoView(true);", el);

// JS click bypassing clickability checks — red flag
js.executeScript("arguments[0].click();", el);

⚠️ Common mistake: "the normal click doesn't fire — I'll do it via JS." That hides a real defect (the element is covered by a modal) and gives you a green test on a broken UI.

07

What is Selenium Grid and why do you need it?

Short answer: Selenium Grid is infrastructure for distributed, parallel test execution across different machines, browsers and OSes. The classic architecture is hub-and-node: the hub accepts requests and hands them to nodes where the browsers actually run. Key point: Grid is an execution layer, not a driver and not a test framework.

In depth:

  1. Hub — the entry point: receives a test request with the required capabilities (browser, version, OS) and routes it to a matching node.
  2. Node — a worker machine where the real browser runs and WebDriver commands execute.
  3. Why — parallelize the run (10 tests on 10 nodes instead of a queue), and cover a cross-browser, cross-platform matrix without a zoo of local machines.
              ┌──────────┐
   tests ───► │   HUB    │  routes by capabilities
              └────┬─────┘
        ┌──────────┼──────────┐
     ┌──┴───┐   ┌──┴────┐  ┌──┴────┐
     │Node 1│   │Node 2 │  │Node 3 │
     │Chrome│   │Firefox│  │ Edge  │
     └──────┘   └───────┘  └───────┘

⚠️ Common mistake: confusing Grid with Selenium WebDriver itself or with the test runner (TestNG/pytest). Grid only distributes execution — you still write and drive the tests with a framework.

08

How does Playwright differ from Selenium?

Short answer: Playwright automatically waits for each element's actionability (visible, enabled, stable) before acting, so manual waits are rarely needed. It drives the browser directly over protocols (CDP and equivalents) rather than through the WebDriver chain — which is faster and more stable — and ships tracing, screenshots and network mocking out of the box.

In depth:

  1. Auto-wait — before a click Playwright itself waits for the element to be visible, enabled and stable; you rarely write an explicit WebDriverWait.
  2. Browser control — a direct protocol instead of JSON Wire / W3C over HTTP; fewer network hops, less flakiness.
  3. Out of the box — a trace viewer, auto screenshots/video, page.route() for network mocking, and auto-waiting assertions (expect).
Criterion Selenium Playwright
Waits manual (WebDriverWait) auto-actionability
Browser link WebDriver protocol direct (CDP, etc.)
Trace/network/video bolt-on built-in
Standard W3C, wider ecosystem newer, unified API

⚠️ Common mistake: carrying the Selenium habit of manual sleep/wait into Playwright. Here it's redundant and often harmful — rely on the built-in auto-waits.

09

What properties should a good automated test have?

Short answer: A good automated test is captured by the FIRST mnemonic: Fast, Independent, Repeatable, Self-validating, Timely. Plus determinism and no hardcoded data. In interviews, the interviewer especially listens for tests being independent of one another — a question actually asked at Ozon, for example.

In depth:

  • Fast — the run shouldn't take forever, or people stop running it.
  • Independent — a test doesn't depend on others or on run order; it sets up and tears down its own state.
  • Repeatable — gives the same result in any environment, not tied to "today's" date or someone else's data.
  • Self-validating — a clear pass/fail via assertions, no manual log reading.
  • Timely — written on time, alongside the feature code, not "someday later."

Also: determinism (no random delays or races) and no hardcoded test data — generate it or prepare it through fixtures.

⚠️ Common mistake: building tests that only pass in a certain order because of shared state. Run them shuffled or in parallel and the suite falls apart.

10

How do you choose a stable locator, and why is an absolute XPath brittle?

Short answer: A stable locator relies on what doesn't change when the markup is restyled. Priority: data-testidid → a semantic attribute or role → a short relative CSS. An absolute XPath like /html/body/div[3]/div/span[2] is brittle because it breaks on any node inserted or reordered in the tree.

In depth:

  1. data-testid — an attribute added specifically for tests; unaffected by design and refactoring. The best choice.
  2. id — stable, as long as it isn't auto-generated (not id="ember1234").
  3. Semantics/rolegetByRole, aria-label, name; robust and close to what the user sees.
  4. Short relative CSS — anchored on a class near the element.
# Bad: an absolute path — breaks on any markup edit
/html/body/div[3]/div/form/div[2]/button

# Good: anchored on a test attribute
[data-testid="checkout-submit"]

Bonus in the answer: agree with developers to add data-testid — cheaper than fighting brittle selectors.

⚠️ Common mistake: copying "Copy full XPath" from DevTools. That absolute path is green today and red after the next markup change.

11

What makes Selenide more convenient than raw Selenium?

Short answer: Selenide is a wrapper over Selenium that fixes its pain points: smart waits by default (a 4-second timeout in every $/$$), terse syntax like $("...").shouldBe(visible), automatic screenshots on failure, and automatic driver management. In Russian Java teams Selenide is almost the standard, and it really does come up in interviews.

In depth:

  1. Smart waits — every lookup and assertion waits up to 4 seconds on its own, so explicit WebDriverWait and sleep are rarely needed — removing the main source of flakiness.
  2. Terse APIshouldBe, shouldHave instead of manual checks; the code reads like a behavior description.
  3. Diagnostics — automatic screenshot and HTML dump on failure, out of the box.
  4. Driver — Selenide starts and closes WebDriver for you.
// Raw Selenium: manual wait + state check
new WebDriverWait(driver, Duration.ofSeconds(4))
    .until(ExpectedConditions.elementToBeClickable(By.id("submit")))
    .click();

// Selenide: waiting is built in
$("#submit").shouldBe(visible).click();

⚠️ Common mistake: treating Selenide as a separate "competitor" to Selenium. It's a wrapper on top of Selenium WebDriver — it doesn't replace it, it makes it more convenient.

Source notes

References and review policy

RecallDeck’s interview answers are editorial material, reviewed against maintained official documentation where a primary reference is available. Tool selections use direct provider links and contain no affiliate placements. Features can change after the review date.

From reading to recall

Practice the full interview loop.

RecallDeck schedules the concepts you miss and keeps coding, design, and behavioral fundamentals available when the interviewer changes direction.

Start studying

Keep going

RecallDeck Interview Library

Detailed answers from the same curated interview deck, organized for search, study, and durable recall.

RSS