Toolmingo
Guides19 min read

50 Selenium Interview Questions (With Answers)

Top Selenium WebDriver interview questions with clear answers and examples — covering architecture, locators, waits, Page Object Model, cross-browser testing, and CI/CD integration.

Selenium interviews test your understanding of browser automation fundamentals, locator strategies, synchronisation, test design patterns, and CI/CD integration. This guide covers the 50 most common questions — with concise answers and real examples.

Quick reference

Topic Most asked questions
Architecture WebDriver, Selenium Grid, RC vs WebDriver
Locators XPath vs CSS, strategies, dynamic elements
Waits Implicit vs explicit vs fluent, timing issues
Actions Keyboard/mouse, frames, windows, alerts
Page Object Model Design pattern, implementation, benefits
Cross-browser Chrome/Firefox/Edge, headless, remote
Assertions JUnit/TestNG/pytest, soft vs hard
CI/CD Jenkins, GitHub Actions, Docker Selenium
Advanced Shadow DOM, JavaScript executor, listeners
Performance Parallel execution, test optimisation

Architecture & Fundamentals

1. What is Selenium and what are its components?

Selenium is an open-source suite for browser automation and web testing:

Component Purpose
Selenium WebDriver Direct browser communication via W3C WebDriver protocol
Selenium IDE Browser extension for record-and-playback
Selenium Grid Parallel/distributed test execution across machines

Selenium WebDriver is the most widely used component in modern automation.

2. Explain the Selenium WebDriver architecture.

Test Script (Python/Java/C#)
        │  W3C WebDriver Protocol (HTTP/JSON Wire)
        ▼
Browser Driver (ChromeDriver / GeckoDriver / EdgeDriver)
        │  Native browser commands
        ▼
Browser (Chrome / Firefox / Edge)

The test script sends REST API commands to the browser driver. The driver translates them to native browser instructions. The result travels back the same way.

3. What is the difference between Selenium RC and Selenium WebDriver?

Aspect Selenium RC Selenium WebDriver
Architecture Server intermediary required Direct browser communication
Speed Slower (server hop) Faster
API Complex (RC commands) Clean OOP API
Multi-window Limited Full support
Maintenance Deprecated Actively maintained
Standard Proprietary W3C standard

Selenium RC is deprecated. Always use WebDriver (Selenium 3/4).

4. What is WebDriverManager and why use it?

WebDriverManager (Bonigarcía) auto-downloads and configures browser drivers, removing manual driver management:

# Without WebDriverManager — manual path needed
from selenium import webdriver
driver = webdriver.Chrome(executable_path='/usr/bin/chromedriver')

# With WebDriverManager — automatic
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

Note: Selenium 4.6+ includes built-in driver manager — no third-party library needed.

5. What is Selenium Grid and when do you use it?

Selenium Grid distributes tests across multiple machines and browsers in parallel:

         ┌─────────────┐
         │    Hub       │  ← receives test requests
         └──────┬──────┘
       ┌────────┼────────┐
  ┌────▼───┐ ┌──▼────┐ ┌─▼────┐
  │Node 1  │ │Node 2 │ │Node 3│
  │Chrome  │ │Firefox│ │Edge  │
  └────────┘ └───────┘ └──────┘

Use cases: cross-browser testing, parallelising large test suites, reducing total execution time.

Selenium Grid 4 uses standalone / hub-node / fully distributed modes and supports Docker out of the box.


Locators

6. What locator strategies does Selenium support?

from selenium.webdriver.common.by import By

driver.find_element(By.ID, "username")
driver.find_element(By.NAME, "email")
driver.find_element(By.CLASS_NAME, "btn-primary")
driver.find_element(By.TAG_NAME, "input")
driver.find_element(By.LINK_TEXT, "Sign In")
driver.find_element(By.PARTIAL_LINK_TEXT, "Sign")
driver.find_element(By.CSS_SELECTOR, "#login-form input[type='email']")
driver.find_element(By.XPATH, "//button[contains(text(),'Submit')]")

Priority (best to worst): ID → CSS Selector → XPath

7. XPath vs CSS Selector — which is better?

Aspect XPath CSS Selector
Speed Slower Faster
Readability Verbose Cleaner
Traverse up (parent) Yes (parent::, ..) No
Text matching Yes (contains(text(),'x')) Limited
Partial attribute Yes (contains(@id,'x')) Yes ([id*='x'])
Browser support All All

Recommendation: Use CSS Selectors by default. Use XPath when you need to traverse upward or match by text content.

8. Write XPath for an element with dynamic ID.

# Dynamic ID like "row_12345_name" — avoid exact match
# ❌ Bad
driver.find_element(By.XPATH, "//input[@id='row_12345_name']")

# ✅ Good — starts-with
driver.find_element(By.XPATH, "//input[starts-with(@id,'row_')]")

# ✅ Good — contains
driver.find_element(By.XPATH, "//input[contains(@id,'name')]")

# ✅ Good — sibling/ancestor relationship
driver.find_element(By.XPATH, "//label[text()='Full Name']/following-sibling::input")

9. What is the difference between find_element and find_elements?

# find_element — returns first match, raises NoSuchElementException if not found
element = driver.find_element(By.CLASS_NAME, "item")

# find_elements — returns list, empty list if not found (no exception)
items = driver.find_elements(By.CLASS_NAME, "item")
if items:
    print(f"Found {len(items)} items")

10. How do you locate elements inside an iframe?

# Switch to iframe first, then locate elements inside it
driver.switch_to.frame("iframe-id")          # by id/name
driver.switch_to.frame(0)                     # by index
driver.switch_to.frame(driver.find_element(By.TAG_NAME, "iframe"))  # by element

element = driver.find_element(By.ID, "inner-element")

# Switch back to main document
driver.switch_to.default_content()
# Switch to parent frame (one level up)
driver.switch_to.parent_frame()

Waits

11. What are the three types of waits in Selenium?

Wait Type Scope Configurable? Exception
Implicit All find_element calls globally timeout only NoSuchElementException
Explicit Specific condition, specific element timeout + polling TimeoutException
Fluent Like explicit but custom polling + ignore exceptions timeout + poll interval + exceptions TimeoutException

12. What is implicit wait and what are its drawbacks?

driver.implicitly_wait(10)  # waits up to 10 seconds for element to appear
element = driver.find_element(By.ID, "username")

Drawbacks:

  • Applies globally — can slow down tests where elements don't exist
  • Interacts badly with explicit waits (unpredictable behaviour)
  • Cannot wait for element state (visible, clickable, etc.)
  • Best practice: avoid implicit waits, use explicit waits.

13. How do you use WebDriverWait (explicit wait)?

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

wait = WebDriverWait(driver, timeout=10)

# Wait for element to be visible
element = wait.until(EC.visibility_of_element_located((By.ID, "result")))

# Wait for element to be clickable
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".submit-btn")))

# Wait for URL to contain a string
wait.until(EC.url_contains("/dashboard"))

# Wait for text to be present
wait.until(EC.text_to_be_present_in_element((By.ID, "status"), "Complete"))

# Wait for element to disappear
wait.until(EC.invisibility_of_element_located((By.ID, "loading-spinner")))

14. What is FluentWait and how does it differ from WebDriverWait?

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException
import time

# FluentWait: custom polling interval + ignore specific exceptions
wait = WebDriverWait(
    driver,
    timeout=30,
    poll_frequency=2,                          # check every 2 seconds
    ignored_exceptions=[NoSuchElementException]
)

element = wait.until(EC.presence_of_element_located((By.ID, "dynamic-content")))

Key difference: FluentWait allows custom polling interval and exception suppression. WebDriverWait uses 500ms polling by default.

15. What is a StaleElementReferenceException and how do you fix it?

Occurs when a previously found element is no longer attached to the DOM (page refreshed, DOM updated).

from selenium.common.exceptions import StaleElementReferenceException

# ✅ Fix 1: re-locate the element
def click_with_retry(driver, locator, retries=3):
    for _ in range(retries):
        try:
            driver.find_element(*locator).click()
            return
        except StaleElementReferenceException:
            time.sleep(0.5)

# ✅ Fix 2: use WebDriverWait to wait for re-appearance
wait.until(EC.staleness_of(old_element))
new_element = driver.find_element(By.ID, "element-id")

User Actions

16. How do you handle dropdowns in Selenium?

from selenium.webdriver.support.ui import Select

# HTML <select> element
select = Select(driver.find_element(By.ID, "country"))

select.select_by_visible_text("Montenegro")
select.select_by_value("ME")
select.select_by_index(5)

# Multi-select
select.select_by_visible_text("Option A")
select.select_by_visible_text("Option B")

# Deselect all
select.deselect_all()
print(select.all_selected_options)
print(select.options)

Note: Select class only works with <select> HTML elements — not custom dropdown components.

17. How do you perform mouse hover, drag-and-drop, and right-click?

from selenium.webdriver.common.action_chains import ActionChains

actions = ActionChains(driver)

# Mouse hover
hover_element = driver.find_element(By.ID, "menu-item")
actions.move_to_element(hover_element).perform()

# Right-click (context menu)
element = driver.find_element(By.ID, "target")
actions.context_click(element).perform()

# Drag and drop
source = driver.find_element(By.ID, "drag-source")
target = driver.find_element(By.ID, "drop-target")
actions.drag_and_drop(source, target).perform()

# Double-click
actions.double_click(element).perform()

# Click and hold, move, release
actions.click_and_hold(source).move_to_element(target).release().perform()

18. How do you handle browser alerts and pop-ups?

from selenium.webdriver.support import expected_conditions as EC

# Wait for alert
wait.until(EC.alert_is_present())
alert = driver.switch_to.alert

# Simple alert — only OK button
alert.accept()

# Confirm dialog — OK or Cancel
alert.accept()    # click OK
alert.dismiss()   # click Cancel

# Prompt dialog — text input
alert.send_keys("input text")
alert.accept()

# Get alert text
print(alert.text)

19. How do you handle multiple browser windows/tabs?

# Get current window handle
original_window = driver.current_window_handle

# Open new tab (Selenium 4)
driver.switch_to.new_window('tab')

# Or click a link that opens new window
driver.find_element(By.LINK_TEXT, "Open in new tab").click()

# Get all window handles
all_windows = driver.window_handles

# Switch to new window
for window in all_windows:
    if window != original_window:
        driver.switch_to.window(window)
        break

# Do stuff in new window
print(driver.title)

# Switch back
driver.switch_to.window(original_window)

# Close current window
driver.close()

20. How do you upload a file in Selenium?

# For <input type="file"> — use send_keys with absolute file path
file_input = driver.find_element(By.CSS_SELECTOR, "input[type='file']")
file_input.send_keys("/absolute/path/to/file.pdf")

# Verify file name appears
print(file_input.get_attribute("value"))  # C:\fakepath\file.pdf (browser security)

Note: For non-standard file upload components (drag-and-drop zones), use JavaScript executor or libraries like PyAutoGUI.


Page Object Model

21. What is the Page Object Model (POM)?

POM is a design pattern where each web page is represented as a class. Element locators and page actions are encapsulated within the class.

# login_page.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class LoginPage:
    URL = "https://example.com/login"

    # Locators
    USERNAME_INPUT = (By.ID, "username")
    PASSWORD_INPUT = (By.ID, "password")
    LOGIN_BUTTON   = (By.CSS_SELECTOR, "button[type='submit']")
    ERROR_MESSAGE  = (By.CLASS_NAME, "error-msg")

    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    def open(self):
        self.driver.get(self.URL)
        return self

    def login(self, username, password):
        self.wait.until(EC.visibility_of_element_located(self.USERNAME_INPUT))
        self.driver.find_element(*self.USERNAME_INPUT).send_keys(username)
        self.driver.find_element(*self.PASSWORD_INPUT).send_keys(password)
        self.driver.find_element(*self.LOGIN_BUTTON).click()
        return self

    def get_error_message(self):
        return self.wait.until(
            EC.visibility_of_element_located(self.ERROR_MESSAGE)
        ).text
# test_login.py
def test_invalid_login(driver):
    page = LoginPage(driver)
    page.open().login("wrong@email.com", "wrongpass")
    assert page.get_error_message() == "Invalid credentials"

22. What are the benefits of Page Object Model?

Benefit Explanation
Reusability Page actions written once, used in many tests
Maintainability Locator change in one place only
Readability Tests describe intent, not browser mechanics
Separation Test logic separated from UI details
Debugging Errors point to page class, not test code

23. What is the Page Factory pattern?

Page Factory (Java) uses @FindBy annotations and PageFactory.initElements():

// Java example
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class LoginPage {
    @FindBy(id = "username")
    private WebElement usernameInput;

    @FindBy(css = "button[type='submit']")
    private WebElement loginButton;

    public LoginPage(WebDriver driver) {
        PageFactory.initElements(driver, this);
    }

    public void login(String user, String pass) {
        usernameInput.sendKeys(user);
        loginButton.click();
    }
}

Python equivalent: Python doesn't have Page Factory natively — use the POM pattern with tuples as shown in Q21.

24. How do you structure a Selenium test project?

project/
├── pages/
│   ├── base_page.py       # common methods (wait, scroll, screenshot)
│   ├── login_page.py
│   ├── dashboard_page.py
│   └── checkout_page.py
├── tests/
│   ├── conftest.py        # pytest fixtures (driver setup/teardown)
│   ├── test_login.py
│   └── test_checkout.py
├── utils/
│   ├── driver_factory.py  # browser/environment config
│   └── helpers.py
├── data/
│   └── test_data.json
├── requirements.txt
└── pytest.ini

25. What is the difference between driver.close() and driver.quit()?

Method Effect
driver.close() Closes the current browser window/tab
driver.quit() Closes all windows and ends the WebDriver session

Best practice: Always call driver.quit() in teardown — it releases the browser driver process. Failure to call quit leaks processes.


Cross-Browser Testing

26. How do you run tests in headless mode?

from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless=new")         # Chrome headless
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--window-size=1920,1080")

driver = webdriver.Chrome(options=options)
# Firefox headless
from selenium.webdriver.firefox.options import Options as FirefoxOptions

ff_options = FirefoxOptions()
ff_options.add_argument("--headless")
driver = webdriver.Firefox(options=ff_options)

27. How do you run the same test in multiple browsers?

# pytest parametrize approach
import pytest
from selenium import webdriver

@pytest.fixture(params=["chrome", "firefox", "edge"])
def driver(request):
    browser = request.param
    if browser == "chrome":
        d = webdriver.Chrome()
    elif browser == "firefox":
        d = webdriver.Firefox()
    elif browser == "edge":
        d = webdriver.Edge()
    yield d
    d.quit()

Or use Selenium Grid — register nodes with different browsers and use RemoteWebDriver:

from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver

capabilities = {"browserName": "firefox", "browserVersion": "latest"}
driver = RemoteWebDriver(
    command_executor="http://localhost:4444/wd/hub",
    desired_capabilities=capabilities
)

28. What is WebDriver BiDi and Selenium 4 improvements?

Selenium 4 key improvements:

Feature Selenium 3 Selenium 4
Protocol JSON Wire Protocol W3C WebDriver standard
CDP Not built-in Chrome DevTools Protocol support
Grid Hub-Node only Standalone/Hub-Node/Fully Distributed
New Window Not standardised driver.switch_to.new_window('tab')
Relative Locators near(), above(), below(), to_left_of()
BiDi Bidirectional communication (event-driven)

Assertions & Reporting

29. How do you take screenshots in Selenium?

# Full page screenshot
driver.save_screenshot("screenshot.png")

# Element screenshot (Selenium 4)
element = driver.find_element(By.ID, "chart")
element.screenshot("chart.png")

# In-memory (for pytest / custom reporting)
import base64
screenshot_base64 = driver.get_screenshot_as_base64()
screenshot_bytes = driver.get_screenshot_as_png()

Best practice: take screenshot on test failure using pytest hooks:

# conftest.py
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    rep = outcome.get_result()
    if rep.when == "call" and rep.failed:
        driver = item.funcargs.get("driver")
        if driver:
            driver.save_screenshot(f"screenshots/{item.name}.png")

30. What is the difference between hard and soft assertions?

# Hard assertion — test stops on first failure
def test_hard():
    assert driver.title == "Expected Title"   # stops here if wrong
    assert driver.current_url == "expected/url"

# Soft assertion — collects all failures, reports at end
# Use pytest-check or softest library
import pytest_check as check

def test_soft():
    check.equal(driver.title, "Expected Title")
    check.is_in("/dashboard", driver.current_url)
    # test continues even if first check fails

JavaScript Executor

31. When and how do you use JavaScript Executor?

from selenium.webdriver.common.by import By

# 1. Click hidden/obscured element
driver.execute_script("arguments[0].click();", element)

# 2. Scroll to element
driver.execute_script("arguments[0].scrollIntoView(true);", element)

# 3. Scroll page
driver.execute_script("window.scrollBy(0, 500);")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

# 4. Set input value (bypasses sendKeys limitations)
driver.execute_script("arguments[0].value = 'test@email.com';", input_el)

# 5. Get inner text
text = driver.execute_script("return arguments[0].innerText;", element)

# 6. Remove readonly attribute
driver.execute_script("arguments[0].removeAttribute('readonly');", el)

# 7. Return value from script
page_title = driver.execute_script("return document.title;")

32. How do you handle Shadow DOM elements?

Shadow DOM encapsulates elements — normal locators can't pierce it.

# Selenium 4 — use CSS selector with shadow root
shadow_host = driver.find_element(By.CSS_SELECTOR, "my-component")
shadow_root = shadow_host.shadow_root
inner_element = shadow_root.find_element(By.CSS_SELECTOR, "input.search")
inner_element.send_keys("test")
# JavaScript approach (older Selenium / deeper shadow roots)
shadow_root = driver.execute_script(
    "return arguments[0].shadowRoot", shadow_host
)
inner = driver.execute_script(
    "return arguments[0].querySelector('input.search')", shadow_root
)

Advanced Topics

33. How do you handle cookies in Selenium?

# Get all cookies
cookies = driver.get_cookies()

# Get specific cookie
session_cookie = driver.get_cookie("session_id")

# Add cookie (must be on the domain first)
driver.get("https://example.com")
driver.add_cookie({"name": "auth_token", "value": "abc123"})

# Delete cookie
driver.delete_cookie("session_id")
driver.delete_all_cookies()

# Cookie injection pattern (bypass login in tests)
def inject_session(driver, token):
    driver.get("https://example.com")
    driver.add_cookie({"name": "session", "value": token, "path": "/"})
    driver.refresh()

34. How do you handle AJAX / dynamic content?

# Wait for AJAX spinner to disappear
wait.until(EC.invisibility_of_element_located((By.ID, "loading")))

# Wait for network idle via JavaScript
wait.until(lambda d: d.execute_script("return document.readyState") == "complete")

# Wait for jQuery AJAX (if site uses jQuery)
wait.until(lambda d: d.execute_script("return jQuery.active") == 0)

# Wait for element count to change
initial_count = len(driver.find_elements(By.CLASS_NAME, "result-item"))
driver.find_element(By.ID, "load-more").click()
wait.until(lambda d: len(d.find_elements(By.CLASS_NAME, "result-item")) > initial_count)

35. How do you capture network traffic / intercept requests?

# Selenium 4 with CDP (Chrome DevTools Protocol)
from selenium.webdriver.common.log import Log

driver.execute_cdp_cmd("Network.enable", {})
driver.execute_cdp_cmd("Network.setExtraHTTPHeaders", {
    "headers": {"Authorization": "Bearer token123"}
})

# Capture requests via CDP (requires threading)
requests_log = []
driver.execute_cdp_cmd("Network.requestWillBeSent", {})
# More complete implementation uses selenium-wire library:
# pip install selenium-wire
from seleniumwire import webdriver as wire_webdriver
driver = wire_webdriver.Chrome()
driver.get("https://example.com")
for request in driver.requests:
    if request.response:
        print(request.url, request.response.status_code)

36. What are Selenium Listeners / Event Firing WebDriver?

// Java — WebDriverEventListener
public class TestListener implements WebDriverEventListener {
    @Override
    public void beforeClickOn(WebElement element, WebDriver driver) {
        System.out.println("Before click: " + element.getText());
    }
    @Override
    public void onException(Throwable throwable, WebDriver driver) {
        driver.findElement(By.tagName("body")); // save screenshot here
    }
}

WebDriver driver = new ChromeDriver();
EventFiringWebDriver efwd = new EventFiringWebDriver(driver);
efwd.register(new TestListener());

Python alternative: subclass WebDriver or use pytest fixtures/hooks.


Test Configuration & CI/CD

37. How do you structure pytest fixtures for Selenium?

# conftest.py
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

@pytest.fixture(scope="session")
def driver():
    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--window-size=1920,1080")
    d = webdriver.Chrome(options=options)
    d.implicitly_wait(0)  # use explicit waits
    yield d
    d.quit()

@pytest.fixture(autouse=True)
def navigate_to_home(driver):
    driver.get("https://example.com")
    yield

Scope: function (default) — new browser per test; session — one browser for all tests (faster but shared state).

38. How do you run Selenium tests in GitHub Actions?

# .github/workflows/selenium.yml
name: Selenium Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: pip install selenium pytest webdriver-manager

      - name: Run tests
        run: pytest tests/ --headless -v

      - name: Upload screenshots on failure
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: screenshots
          path: screenshots/

39. How do you run Selenium in Docker?

# docker-compose.yml — Selenium Grid
services:
  selenium-hub:
    image: selenium/hub:4
    ports:
      - "4444:4444"

  chrome:
    image: selenium/node-chrome:4
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_NODE_MAX_SESSIONS=5

  tests:
    build: .
    depends_on:
      - chrome
    environment:
      - SELENIUM_HUB=http://selenium-hub:4444
    command: pytest tests/ -v
# driver_factory.py
import os
from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

hub = os.getenv("SELENIUM_HUB", "http://localhost:4444")
driver = RemoteWebDriver(
    command_executor=f"{hub}/wd/hub",
    desired_capabilities=DesiredCapabilities.CHROME,
)

40. How do you run tests in parallel with pytest-xdist?

pip install pytest-xdist

# Run with 4 workers
pytest tests/ -n 4

# Run one worker per CPU core
pytest tests/ -n auto
# Each worker needs its own browser — use function-scoped fixtures
@pytest.fixture(scope="function")
def driver():
    d = webdriver.Chrome()
    yield d
    d.quit()

Note: session-scoped fixtures are shared per worker, not across all workers.


Common Debugging & Patterns

41. How do you debug a flaky test?

Flaky tests pass sometimes, fail others. Common causes and fixes:

Cause Fix
Race condition Use explicit waits, not time.sleep()
Stale element Re-locate element after DOM update
Overlapping element Scroll into view / wait for overlay to close
Network latency Increase timeout / wait for network idle
Test order dependency Isolate tests, use fresh state per test
Browser version mismatch Pin driver + browser version

42. How do you handle ElementNotInteractableException?

from selenium.common.exceptions import ElementNotInteractableException

# Element exists but not visible/enabled
# Fix 1: Wait until clickable
wait.until(EC.element_to_be_clickable((By.ID, "submit")))

# Fix 2: Scroll into view
driver.execute_script("arguments[0].scrollIntoView(true);", element)
element.click()

# Fix 3: JavaScript click (last resort)
driver.execute_script("arguments[0].click();", element)

# Fix 4: Move to element first
ActionChains(driver).move_to_element(element).click().perform()

43. How do you verify an element is NOT present?

# find_elements returns empty list — no exception
elements = driver.find_elements(By.ID, "error-message")
assert len(elements) == 0, "Error message should not be visible"

# Check visibility
def is_element_visible(driver, locator):
    elements = driver.find_elements(*locator)
    return len(elements) > 0 and elements[0].is_displayed()

# Wait for element to be invisible
wait.until(EC.invisibility_of_element_located((By.ID, "spinner")))

44. How do you get browser logs / console errors?

# Chrome browser logs (requires logging capability)
from selenium.webdriver.chrome.options import Options

options = Options()
options.set_capability("goog:loggingPrefs", {"browser": "ALL"})
driver = webdriver.Chrome(options=options)

driver.get("https://example.com")
logs = driver.get_log("browser")
errors = [log for log in logs if log["level"] == "SEVERE"]
print(errors)

45. How do you simulate keyboard shortcuts?

from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

# Send keys to element
input_el.send_keys(Keys.RETURN)        # press Enter
input_el.send_keys(Keys.TAB)           # Tab
input_el.send_keys(Keys.BACKSPACE)
input_el.send_keys(Keys.CONTROL, "a")  # Ctrl+A (select all)

# Clear and type
input_el.send_keys(Keys.CONTROL + "a")
input_el.send_keys(Keys.DELETE)
input_el.send_keys("new text")

# ActionChains for combinations
actions = ActionChains(driver)
actions.key_down(Keys.CONTROL).send_keys("c").key_up(Keys.CONTROL).perform()

Performance & Best Practices

46. What are best practices for writing maintainable Selenium tests?

Practice Detail
Use POM Separate locators from test logic
Explicit waits only Never time.sleep(), avoid implicit waits
Meaningful locators Prefer IDs, data-testid attributes
Test isolation Each test independent, own setup/teardown
Screenshot on failure Built-in in conftest fixture
Environment config URLs, credentials from env vars, not hardcoded
Small, focused tests One concept per test
Retry flaky tests pytest-rerunfailures for known-unstable flows

47. How do you add data-testid attributes for better selectors?

<!-- In production code, add data-testid attributes -->
<button data-testid="checkout-submit">Pay Now</button>
<input data-testid="email-input" type="email" />
# Stable locators that won't break on CSS/text changes
driver.find_element(By.CSS_SELECTOR, "[data-testid='checkout-submit']")
driver.find_element(By.CSS_SELECTOR, "[data-testid='email-input']")

48. Selenium vs Playwright vs Cypress — when to use which?

Aspect Selenium Playwright Cypress
Language support Python/Java/C#/JS/Ruby Python/JS/Java/.NET JavaScript only
Speed Slower Fast Fast
Shadow DOM CDP support Native Limited
Network interception selenium-wire Built-in Built-in
Cross-browser Chrome/Firefox/Edge/Safari Chrome/Firefox/WebKit Chrome/Firefox/Edge
Mobile Limited Android support Limited
Learning curve Moderate Low-Moderate Low
Best for Legacy, multi-language teams Modern automation Frontend dev TDD
iframe support Yes Yes Complex
CI/Docker Selenium Grid Docker image Docker image

Choose Selenium when you need multi-language support, Java team, or must test Safari and IE legacy.

49. How do you test file downloads?

import os
from selenium.webdriver.chrome.options import Options

# Configure download directory
download_dir = "/tmp/downloads"
os.makedirs(download_dir, exist_ok=True)

options = Options()
prefs = {
    "download.default_directory": download_dir,
    "download.prompt_for_download": False,
    "download.directory_upgrade": True,
    "safebrowsing.enabled": True
}
options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=options)

# Click download button
driver.find_element(By.ID, "download-report").click()

# Wait for file to appear
import time
timeout = 30
start = time.time()
while time.time() - start < timeout:
    files = os.listdir(download_dir)
    if files and not any(f.endswith(".crdownload") for f in files):
        break
    time.sleep(1)

assert any(f.endswith(".csv") for f in os.listdir(download_dir))

50. What are the most common Selenium anti-patterns?

Anti-pattern Problem Fix
time.sleep() everywhere Slow, brittle Explicit WebDriverWait
XPath with position /tr[1] Breaks on reorder Use unique attribute
driver.implicitly_wait() mixed with explicit Unpredictable behaviour Use explicit waits only
No POM / all locators in tests Hard to maintain Implement Page Object Model
Session-scope driver with state Tests interfere Function-scope or clean state
find_element in assertions StaleElementReference Re-locate inside assertion
Hardcoded URLs and credentials Not portable Environment variables
No screenshot on failure Hard to debug failures pytest hookimpl screenshot

Comparison tables

Selenium vs alternatives

Feature Selenium 4 Playwright Cypress Puppeteer
Protocol W3C WebDriver CDP/WebSocket CDP CDP
Languages Python/Java/C#/Ruby/JS Python/JS/Java/.NET JS/TS JS/TS
Safari Yes (limited) WebKit No No
Parallel Grid / pytest-xdist Built-in Dashboard (paid) Manual
Auto-wait ❌ (manual)
Network mock selenium-wire
Trace/video
Mobile emulation Limited

Common wait conditions

Condition expected_conditions method
Element present in DOM presence_of_element_located
Element visible visibility_of_element_located
Element clickable element_to_be_clickable
Element invisible invisibility_of_element_located
Text in element text_to_be_present_in_element
URL contains url_contains
Title contains title_contains
Alert present alert_is_present
Frame available frame_to_be_available_and_switch_to_it
Number of windows number_of_windows_to_be

FAQ

Q: Is Selenium 3 still usable?
Selenium 3 works but is in maintenance-only mode. Selenium 4 brings W3C standardisation, CDP support, Selenium Grid 4, and relative locators — upgrade when possible.

Q: Can Selenium test mobile apps?
Not natively. Use Appium (built on the WebDriver protocol) for iOS and Android app automation. Appium reuses Selenium client libraries, so the API is familiar.

Q: How do I handle CAPTCHAs in Selenium?
CAPTCHAs are designed to block automation. Options: disable CAPTCHA in test environments, use test API keys from reCAPTCHA (v2 test keys), or use third-party solving services. Never try to "bypass" production CAPTCHAs.

Q: What is desired_capabilities and is it still used?
DesiredCapabilities was the old way to configure browsers. Selenium 4 replaces it with Options classes (ChromeOptions, FirefoxOptions) and W3C capabilities. DesiredCapabilities still works but is deprecated.

Q: How do I avoid the NoSuchElementException?
Always use explicit waits (WebDriverWait + EC) instead of finding elements immediately after navigation. Pages load asynchronously — the element may not exist yet when your code runs.

Q: Selenium vs selenium-wire vs undetected-chromedriver?

  • selenium-wire — extends Selenium to capture/modify network requests
  • undetected-chromedriver — patches ChromeDriver to avoid bot detection on some sites
  • Use these only when standard Selenium is insufficient; standard Selenium should be the default.

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools