Top 100 Selenium Interview Questions
Real-world, scenario-based Q&A featuring Selenium 4 + Java code snippets, synchronization patterns, stale elements, and headless CI/CD gotchas.
Dynamic Locator Keeps Changing on Refresh
Dynamic elements are extremely common in modern frameworks (React, Angular, Vue) where class names or IDs are auto-generated on load (e.g., id="btn-submit_9a7c3"). A professional automation engineer identifies the root cause and bypasses these dynamic properties by leveraging stable custom attributes, utilizing relative XPath axes, or using text-matching selectors rather than brittle auto-generated values.
Key Takeaways & Core Strategy
- βAvoid dynamic attributes (like auto-generated IDs or class names)
- βIdentify a stable custom data-attribute (e.g., data-testid, data-qa)
- βBuild smart Relative XPath using contains(), starts-with() or text()
- βUtilize relative parent-child-sibling relationships in DOM tree
- βCollaborate with developers to add stable hooks for testing
β οΈ Senior Engineering Warning
Never hardcode dynamic IDs (like btn_4920a) or rely on absolute XPath paths starting from the html root. They will break your test suite on every backend deployment or slight DOM modification.
Deep Dive Explanation
If no stable attributes exist, always inspect the DOM for surrounding static elements. Connect to them first as an anchor, and navigate to the target element using axes like following-sibling, parent, or descendant.
// β brittle dynamic ID: driver.findElement(By.id("btn-submit_9a7c3"));
// β
Best practice: Locate using custom data-attributes
WebElement submitBtn = driver.findElement(By.xpath("//button[@data-testid='submit-login']"));
// β
Sibling Strategy: Locate an input field based on its stable text label
WebElement emailField = driver.findElement(By.xpath("//label[text()='Email']/following-sibling::input"));
// β
Match Partial Dynamic Properties using XPath functions
WebElement partialBtn = driver.findElement(By.xpath("//input[contains(@id, 'login-btn_')]"));Locating Elements with No ID, Name, or Stable XPath
When standard attributes like ID or Name are absent, you must look for accessibility elements or positional relationships. Selenium 4 introduces Relative Locators, allowing you to find elements based on their spatial orientation to other stable, known elements. Additionally, you can utilize text-based XPath queries or narrow down search context by first locating a stable container parent.
Key Takeaways & Core Strategy
- βLeverage accessibility attributes (aria-label, placeholder, title)
- βEmploy Selenium 4 Relative Locators (above, below, toLeftOf, toRightOf, near)
- βConstruct robust text-based XPath matching with normalize-space()
- βNavigate relative DOM hierarchy using stable parent containers
β οΈ Senior Engineering Warning
If your XPath is highly complex (e.g., with more than 3-4 nested descendant levels), it is a major warning sign. It makes your tests fragile and hard to maintain.
Deep Dive Explanation
Relative locators are excellent for forms and grids. Always make sure to combine them with standard explicit waits to guarantee the reference element is visible before locating the target.
import static org.openqa.selenium.support.locators.RelativeLocator.*;
// β
Locate stable reference point
WebElement loginHeader = driver.findElement(By.xpath("//h2[text()='Login']"));
// β
Selenium 4 Relative Locators: Find tag near stable element
WebElement usernameField = driver.findElement(with(By.tagName("input")).below(loginHeader));
// β
Locate relative to stable sibling
WebElement submitButton = driver.findElement(with(By.tagName("button")).below(usernameField));
// β
XPath Text matching with space normalization
WebElement welcomeMsg = driver.findElement(By.xpath("//div[normalize-space()='Welcome back, Guest']"));Element is Not Clickable at Point Error
The "Element is not clickable at point" error occurs when a dynamic overlay (like a loading spinner, cookie consent banner, or sticky header) blocks the element, or it is off-screen. To resolve this, you must explicitly wait for the blocking overlay to become invisible, scroll the element into view, or use JavaScript Click only when the blockage is a known, non-actionable overlay.
Key Takeaways & Core Strategy
- βUnderstand ElementClickInterceptedException causes (overlays, loading spinners)
- βApply Explicit Wait for elementToBeClickable to clear loaders
- βScroll the element into the viewport center using JavaScript Executor
- βUse JavaScript Executor click as a controlled fallback strategy
β οΈ Senior Engineering Warning
Using JavascriptExecutor to click elements as a default fix is an anti-pattern. JS click bypasses browser safety restrictions, meaning it will click hidden, disabled, or covered elements, failing to test the actual user experience.
Deep Dive Explanation
Always debug this by taking a screenshot on failure. If a loader or modal is visible in the screenshot, you need to add an explicit wait for that spinner to disappear before clicking.
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;
// β
Best Practice: Wait for overlapping spinner to disappear first
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.className("loading-spinner")));
// β
Explicit Wait for element to become clickable
WebElement button = wait.until(ExpectedConditions.elementToBeClickable(By.id("btn-submit")));
// β
Scroll to element prior to click (fixes sticky header overlap)
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView({block: 'center'});", button);
button.click();
// β
Fallback Only: Perform direct JavaScript click
((JavascriptExecutor) driver).executeScript("arguments[0].click();", button);Handling Stale Element Reference Exception
A StaleElementReferenceException is thrown when a previously located element is no longer attached to the page DOM. This happens when the page refreshes, an AJAX request replaces a container, or a framework like React tears down and recreates elements. Because the reference ID held by the driver is now invalid, you must re-query the DOM to fetch the new, valid element reference.
Key Takeaways & Core Strategy
- βIdentify causes (page refresh, AJAX container update, DOM replacement)
- βNever store active WebElement references across page transitions
- βImplement a robust try-catch retry mechanism to re-locate the element
- βUse WebDriverWait with ExpectedConditions.refreshed() to auto-recover
β οΈ Senior Engineering Warning
Avoid using @CacheLookup in Selenium Page Factory for elements that refresh dynamically. Caching references causes StaleElementReferenceException the moment the element is detached and rebuilt.
Deep Dive Explanation
Always design your Page Objects to re-fetch elements dynamically within action methods instead of storing them as class variables. This naturally prevents stale references.
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
// β
Solution 1: Use WebDriverWait.refreshed to auto-locate on staleness
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.refreshed(
ExpectedConditions.elementToBeClickable(By.id("save-button"))
)).click();
// β
Solution 2: Implement a clean retry loop for dynamic tables
public void clickDynamicElement(By locator, int maxAttempts) {
int attempts = 0;
while (attempts < maxAttempts) {
try {
driver.findElement(locator).click();
return;
} catch (StaleElementReferenceException e) {
attempts++;
}
}
throw new StaleElementReferenceException("Failed to click element after " + maxAttempts + " retries.");
}Random Popups and Dynamic Ad Interruption
Random popups (like promotional offers, newsletters, or GDPR cookie compliance forms) can appear unexpectedly and block the main UI thread. A professional SDET manages this by configuring the browser driver options to disable notifications, utilizing quick conditional checks with `driver.findElements()` (which does not throw exceptions if empty), or using low-timeout try-catch blocks.
Key Takeaways & Core Strategy
- βDisable popups, notifications, and ads via browser profile settings
- βCheck for popup presence safely using driver.findElements() without delays
- βUtilize try-catch blocks with low timeouts to handle optional modals
- βImplement a teardown/setup hook in TestNG/JUnit to dismiss overlays
β οΈ Senior Engineering Warning
Never put a standard explicit wait (like 10 seconds) on a random popup. If the popup does not appear, your tests will waste 10 seconds waiting, adding massive, unnecessary latency to your test suite.
Deep Dive Explanation
Using findElements() is the fastest, cleanest way to handle optional elements because it does not trigger the implicit wait timeout when looking for an element that might not exist.
import org.openqa.selenium.chrome.ChromeOptions;
import java.util.List;
// β
Configuration Strategy: Disable ads & popups at browser start
ChromeOptions options = new ChromeOptions();
options.addArguments("--disable-popup-blocking");
options.addArguments("--disable-notifications");
// β
Runtime Check: Handle popup instantly without throwing errors
public void dismissCookieBanner() {
// findElements returns empty list immediately if not present, avoiding 10s wait
List<WebElement> banners = driver.findElements(By.cssSelector("#gdpr-cookie-consent"));
if (!banners.isEmpty() && banners.get(0).isDisplayed()) {
banners.get(0).click();
System.out.println("GDPR banner dismissed.");
}
}
// β
Optional Modal Strategy: Fast-fail wait
public void closeNewsletterModal() {
try {
// Wait only 2 seconds for optional popup
new WebDriverWait(driver, Duration.ofSeconds(2))
.until(ExpectedConditions.elementToBeClickable(By.id("close-newsletter")))
.click();
} catch (TimeoutException e) {
// Modal did not show up, proceed without failing
}
}Test Passes Locally but Fails in Headless CI/CD
CI/CD servers are usually headless (no GUI) and operate on shared virtualized hardware with lower resource allocations. These differences often lead to timing inconsistencies, responsive layouts breaking (hiding desktop menus), or dynamic components failing to load in time. To fix this, always set explicit viewport arguments, ensure environment versions align, and capture screen states on failures.
Key Takeaways & Core Strategy
- βConfigure explicit window-size arguments to match desktop resolutions
- βAlign local browser version with CI/CD runner browser binary version
- βIncrease explicit timeouts slightly in CI to account for resource limits
- βImplement automatic screenshot capturing on test failure listener
β οΈ Senior Engineering Warning
Do not rely on driver.manage().window().maximize() in headless CI pipelines. In virtual environments, maximize() often defaults to a very small, restricted layout (like 800x600), hiding responsive menus.
Deep Dive Explanation
Setting a fixed screen resolution like 1920x1080 is critical. Modern web pages are responsive, and if your headless browser defaults to a small size, elements will collapse into hamburger menus, breaking your desktop locators.
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.chrome.ChromeDriver;
// β
Configure Headless options properly for CI pipeline
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless=new"); // Use modern headless engine
options.addArguments("--window-size=1920,1080"); // Force standard desktop layout
options.addArguments("--no-sandbox"); // Required for Linux container permission bypass
options.addArguments("--disable-dev-shm-usage"); // Prevents shared memory crashes in Docker
options.addArguments("--disable-gpu"); // Recommended for server execution
WebDriver driver = new ChromeDriver(options);Handling Extremely Slow Page Loads
Selenium's default page load strategy blocks the test thread until the browser completely fetches and parses all subresources (images, third-party trackers, scripts). If a slow tracker hangs, the entire test blocks and fails with a page load timeout. You can optimize this by changing the Page Load Strategy to "eager" (which proceeds once the HTML DOM is interactive) or waiting for document.readyState via Javascript.
Key Takeaways & Core Strategy
- βConfigure custom pageLoadTimeout values on the driver object
- βUtilize EAGER page load strategy to ignore slow trackers and styling
- βImplement programmatical Javascript waits for document.readyState
- βTrack load times and capture network HAR logs for bottleneck analysis
β οΈ Senior Engineering Warning
Avoid inflating your pageLoadTimeout limit (e.g. to 120 seconds) to bypass slow loading. This masks real performance issues in your application. Instead, switch to eager loading or consult the development team.
Deep Dive Explanation
PageLoadStrategy.EAGER is a game changer for sites with slow ads or analytics trackers. It allows your tests to run the moment the functional HTML layout is ready.
import org.openqa.selenium.PageLoadStrategy;
import org.openqa.selenium.chrome.ChromeOptions;
// β
Set Page Load Strategy to EAGER (Stops waiting for images/trackers)
ChromeOptions options = new ChromeOptions();
options.setPageLoadStrategy(PageLoadStrategy.EAGER);
WebDriver driver = new ChromeDriver(options);
// β
Configure explicit page load timeout limit (e.g., 20 seconds)
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(20));
// β
Wait programmatically for Javascript document load completion
public void waitForPageToLoad() {
new WebDriverWait(driver, Duration.ofSeconds(15)).until(
d -> ((JavascriptExecutor) d)
.executeScript("return document.readyState").equals("complete")
);
}Selecting Dynamically Loaded AJAX Dropdown Options
Modern web applications use custom styling tags (like div, ul, li) to render dropdowns, fetching data dynamically via AJAX only after the user clicks the field. Standard Select commands do not work here. You must first click the container to trigger the API, explicitly wait for option elements to be visible, and click the option matching your text.
Key Takeaways & Core Strategy
- βTrigger dropdown options load by clicking the wrapper first
- βAvoid Selenium Select class on custom React/Angular divs or lists
- βUse visibilityOfAllElementsLocatedBy to wait for dynamic elements
- βIterate option lists dynamically to click matching target text value
β οΈ Senior Engineering Warning
Never use the Select helper class on custom divs or ul/li list containers. The Select class only works with standard HTML <select> tags, throwing an UnexpectedTagNameException if applied elsewhere.
Deep Dive Explanation
Always wait for the visibility of the options list, not just presence in DOM. If you attempt to click an option that is present but hidden, Selenium will throw an ElementNotInteractableException.
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.util.List;
public void selectDynamicAjaxOption(By dropdownHost, By optionLocator, String targetText) {
// 1. Click dropdown to open and trigger AJAX network request
driver.findElement(dropdownHost).click();
// 2. Wait explicitly until dropdown options list becomes visible
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
List<WebElement> options = wait.until(
ExpectedConditions.visibilityOfAllElementsLocatedBy(optionLocator)
);
// 3. Dynamic selection loop
for (WebElement option : options) {
if (option.getText().trim().equalsIgnoreCase(targetText)) {
option.click();
return;
}
}
throw new NoSuchElementException("Failed to find dynamic option: " + targetText);
}Flakiness Caused by Network Latency and API Delays
Slow backend services or network delay will cause automation scripts to fail if they look for elements before they exist. Using static Thread.sleep() delays is a poor solution that increases execution times. Implementing dynamic Explicit and Fluent waits allows the framework to poll the DOM frequently, immediately resuming as soon as the element is ready.
Key Takeaways & Core Strategy
- βAbolish static Thread.sleep() sleeps across the entire framework
- βUse WebDriverWait to apply non-blocking waits for specific conditions
- βImplement FluentWait to ignore exceptions and customize polling rates
- βConfigure appropriate global implicit timeouts with caution
β οΈ Senior Engineering Warning
Mixing implicit and explicit waits in the same framework is strongly discouraged by the Selenium team. Doing so causes unpredictable wait durations, sometimes turning a 10-second timeout into a 90-second delay.
Deep Dive Explanation
Explicit waits are active, non-blocking conditions. If an API responds in 50 milliseconds, the test proceeds instantly, ensuring maximum suite speed compared to hardcoded sleeps.
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
import java.util.NoSuchElementException;
// β
Build a customized Fluent Wait ignoring specific exceptions
Wait<WebDriver> fluentWait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(15))
.pollingEvery(Duration.ofMillis(500)) // Poll DOM every 500ms
.ignoring(NoSuchElementException.class)
.ignoring(StaleElementReferenceException.class);
// β
Dynamic condition: Wait until element is loaded and contains non-empty text
WebElement dynamicValue = fluentWait.until(d -> {
WebElement el = d.findElement(By.id("dashboard-metrics"));
String val = el.getText();
return (!val.isEmpty() && !val.equals("Loading...")) ? el : null;
});Interacting with encapsulated Shadow DOM Elements
Shadow DOM encapsulates web component structures, isolating them from the main DOM document tree. Standard locators and XPath cannot see past this boundary. In Selenium 4, you can call getShadowRoot() on the shadow host element to obtain its SearchContext. From there, you can query inner child elements directly using CSS Selectors.
Key Takeaways & Core Strategy
- βAccess Shadow DOM elements via Selenium 4 getShadowRoot() API
- βQuery inside shadow contexts strictly with CSS selectors (no XPath)
- βUse JavascriptExecutor script queries as a fallback compatibility method
- βTraverse multiple layers of nested shadow roots sequentially
β οΈ Senior Engineering Warning
Standard XPath expressions starting with // cannot pierce the Shadow DOM boundary. Trying to locate a shadow element with standard XPath will result in a NoSuchElementException every time.
Deep Dive Explanation
Always inspect the element in Chrome DevTools first. If you see "#shadow-root (open)", it means the element is encapsulated. Retrieve the parent "Shadow Host" element first, then use getShadowRoot() to find the internal target.
import org.openqa.selenium.SearchContext;
// β
Selenium 4: Accessing Shadow DOM directly
WebElement shadowHost = driver.findElement(By.cssSelector("theme-settings-panel"));
// Get the encapsulated search context
SearchContext shadowContext = shadowHost.getShadowRoot();
// Locate internal element inside the shadow root (CSS selector ONLY, XPath not supported)
WebElement darkModeToggle = shadowContext.findElement(By.cssSelector("button#dark-mode-toggle"));
darkModeToggle.click();
// β
Fallback: Access Shadow DOM via Javascript Executor (highly compatible)
WebElement toggleViaJS = (WebElement) ((JavascriptExecutor) driver).executeScript(
"return document.querySelector('theme-settings-panel').shadowRoot.querySelector('button#dark-mode-toggle')"
);
toggleViaJS.click();Finished practicing?
Head back to the main lobby to explore more interview prep tracks and dashboard tools.
Transitioning to Playwright?
Master ARIA locators, isolated browser contexts, storage state persistence, fixtures, and dynamic network interception.