Selenium questions test hands-on experience, not textbook knowledge. For every question here, notice how the answer includes "In my project..." or "In my framework...". That is not decoration — it is the difference between a hire and a reject. Interviewers at every company, from TCS to Amazon, want proof you have actually done this.
Q: What locator strategies are available in Selenium? Which do you prefer?
A: Eight locators: id (fastest, most reliable), name, className, tagName, linkText, partialLinkText, cssSelector, and xpath. My priority order: id first — it is unique and fast. cssSelector second — powerful syntax, faster than XPath in most browsers. XPath as fallback — when I need text-based matching (//button[text()="Submit"]), parent traversal (//input[@id="email"]/parent::div), or complex conditions. I avoid className for elements with multiple classes (it matches only single class names). I never use tagName alone — too generic. In my framework, about 60% of locators are CSS selectors, 30% are XPath, and 10% are id. The key is consistency within the team — we agreed on CSS-first and documented it in our framework coding standards.
Q: XPath vs CSS Selector — when do you use each?
A: CSS Selector: faster performance, cleaner syntax, supports attribute matching ([id*="partial"], [class^="starts"]), child/descendant selectors (div > input, div input), and pseudo-classes (:first-child, :nth-child(2)). Cannot traverse upward to parents and cannot match by text content. XPath: can traverse in any direction — parent (//input/parent::div), ancestor (//td[text()="John"]/ancestor::tr), following-sibling, preceding-sibling. Supports text matching (text(), contains(text(),"partial")). More verbose syntax. My rule: start with CSS. Switch to XPath when I need to find an element by visible text, navigate to a parent or sibling, or build complex multi-condition locators like //table//tr[td[text()="John"]]//button[@class="edit"]. In practice, XPath is essential for dynamic tables where I locate a row by cell text and then click a button in that row.
Q: How do you handle dynamic elements?
A: Dynamic elements have attributes that change on every page load — like id="user_12345" where the number changes. Strategies: (1) Partial attribute matching in CSS: [id*="user_"], [class^="btn-"], [id$="_submit"]. (2) XPath functions: //div[contains(@id, "user_")], //input[starts-with(@name, "field_")]. (3) Stable parent approach: find a stable ancestor and navigate down — //div[@class="user-panel"]//input. (4) Relative locators (Selenium 4): RelativeLocator.with(By.tagName("input")).near(By.xpath("//label[text()='Email']")). (5) Use data-testid or data-qa attributes — ask developers to add these for automation. This is the best long-term solution. In my project, I coordinated with the dev team to add data-testid attributes to key elements, which eliminated 80% of our dynamic locator problems.
Q: What is the difference between implicit wait, explicit wait, and fluent wait?
A: Implicit wait: global timeout for all findElement calls. driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)). Polls the DOM until the element is present. Simple but imprecise — only checks presence, not visibility or clickability. Explicit wait: targets a specific element with a specific condition. new WebDriverWait(driver, Duration.ofSeconds(10)).until(ExpectedConditions.elementToBeClickable(By.id("btn"))). Precise and flexible. Fluent wait: explicit wait with configurable polling interval and exception ignoring. new FluentWait<>(driver).withTimeout(Duration.ofSeconds(30)).pollingEvery(Duration.ofMillis(500)).ignoring(NoSuchElementException.class). In my framework, I use ONLY explicit waits. No implicit wait — mixing implicit and explicit causes unpredictable behavior. No Thread.sleep() — it wastes time or is not long enough. My BasePage wraps all waits: waitForVisible(), waitForClickable(), waitForTextPresent().
Q: How do you handle dropdowns in Selenium?
A: Two types: standard HTML <select> and custom dropdowns. For <select>: use the Select class — Select dropdown = new Select(driver.findElement(By.id("country"))). Three selection methods: selectByVisibleText("India"), selectByValue("IN"), selectByIndex(2). Get all options: dropdown.getOptions(). Check multi-select: dropdown.isMultiple(). For custom dropdowns (div/ul/li, React Select, Material UI): the Select class does not work. I click the trigger to open it, wait for options to render, then click the desired option: driver.findElement(By.cssSelector(".dropdown-toggle")).click(); wait.until(EC.visibilityOfElementLocated(By.xpath("//li[text()='India']"))).click(). In my framework, I have separate utility methods for each type: selectFromDropdown(By locator, String text) and selectFromCustomDropdown(By trigger, String optionText).
Q: How do you handle alerts, iframes, and multiple windows?
A: Alerts: switch to the alert — Alert alert = driver.switchTo().alert(). Use accept() for OK, dismiss() for Cancel, getText() to read, sendKeys() for prompt input. Always wrap with wait: wait.until(ExpectedConditions.alertIsPresent()). Iframes: Selenium cannot see inside an iframe until you switch — driver.switchTo().frame("frameName"), frame(0) by index, or frame(WebElement). Switch back with driver.switchTo().defaultContent(). For nested iframes, switch to outer first, then inner. Windows: every window has a unique handle. Store the original: String main = driver.getWindowHandle(). After a new window opens: iterate driver.getWindowHandles(), switch to the new one, do your work, close() it, switch back to main. In Selenium 4: driver.switchTo().newWindow(WindowType.TAB) opens a new tab directly. I have utility methods for all three in my BasePage.
Q: What is the Actions class? When do you use it?
A: The Actions class handles advanced user interactions that go beyond simple click and type. Actions actions = new Actions(driver). Mouse hover: actions.moveToElement(menuItem).perform() — essential for dropdown menus that appear on hover. Double-click: actions.doubleClick(element).perform(). Right-click: actions.contextClick(element).perform(). Drag and drop: actions.dragAndDrop(source, target).perform(). Keyboard combinations: actions.keyDown(Keys.CONTROL).click(link1).click(link2).keyUp(Keys.CONTROL).perform() — multi-select. Chain actions: actions.moveToElement(menu).click().moveToElement(submenu).click().perform(). In my project, I used Actions for: hovering over navigation menus, drag-and-drop reordering in a dashboard, and Shift+Click for multi-row selection in a data table.
Q: How do you take screenshots in Selenium?
A: Cast the driver to TakesScreenshot: TakesScreenshot ts = (TakesScreenshot) driver; File src = ts.getScreenshotAs(OutputType.FILE); FileUtils.copyFile(src, new File("screenshots/login_fail_" + timestamp + ".png")). For element-level screenshot (Selenium 4): element.getScreenshotAs(OutputType.FILE). In my framework, screenshots are automatic on failure — I implement TestNG ITestListener.onTestFailure() which captures the screenshot and attaches it to the Allure report using @Attachment. I also capture screenshots at critical test steps for visual evidence. File naming convention: methodName_timestamp.png to avoid overwrites. The screenshots directory is in .gitignore.
Q: What is JavascriptExecutor? When do you use it?
A: JavascriptExecutor lets you run JavaScript directly in the browser. Cast the driver: JavascriptExecutor js = (JavascriptExecutor) driver. Common uses: (1) Click a hidden or covered element: js.executeScript("arguments[0].click();", element). (2) Scroll to element: js.executeScript("arguments[0].scrollIntoView({block:'center'});", element). (3) Set value directly: js.executeScript("arguments[0].value='test';", input) — useful for readonly fields. (4) Check page load state: js.executeScript("return document.readyState"). (5) Highlight for debugging: js.executeScript("arguments[0].style.border='3px solid red';", element). I use JavascriptExecutor as a last resort — when standard Selenium methods fail because of overlapping elements, shadow DOM, or framework-specific rendering. Always try the standard approach first. JS click bypasses visibility checks, which can mask real UI bugs.
Q: What is StaleElementReferenceException and how do you fix it?
A: This exception means the WebElement you found earlier no longer exists in the DOM — the page refreshed, JavaScript re-rendered the element, or the element was removed and re-added. Your reference is "stale" because it points to a DOM node that is gone. Fixes: (1) Re-locate the element — store the locator (By object), not the WebElement, and call findElement() fresh before each interaction. (2) Use refreshed ExpectedCondition: wait.until(ExpectedConditions.refreshed(EC.elementToBeClickable(By.id("btn")))). (3) Retry pattern — wrap the interaction in a loop that catches StaleElementReferenceException and retries 2-3 times. In my POM, I re-find elements inside each method: public void clickLogin() { wait.until(EC.elementToBeClickable(loginButton)).click(); } — the wait re-locates the element each time. This prevents stale references by design.
Q: What is NoSuchElementException? How do you debug it?
A: Thrown when Selenium cannot find an element matching the locator. Debugging checklist: (1) Wrong locator — inspect the element in DevTools, verify the locator in the console with $() for CSS or $x() for XPath. (2) Element not loaded yet — add an explicit wait before the findElement call. (3) Element inside an iframe — switch to the iframe first. (4) Element in a different window/tab — check window handles. (5) Dynamic attribute — the id or class changed since you wrote the locator. Use partial matching. (6) Wrong page — verify the URL to make sure navigation happened. (7) Shadow DOM — element is inside a shadow root, needs special handling. In my framework, I always use explicit waits before finding elements, which eliminates most "not loaded yet" issues. For the rest, the screenshot captured on failure usually shows the root cause immediately.
Q: What is the difference between close() and quit()?
A: driver.close() closes only the current window/tab. If multiple windows are open, the others remain. The WebDriver session may still be active. driver.quit() closes ALL windows and ends the WebDriver session — kills the browser process and the driver executable. Always use quit() in your @AfterMethod or @AfterClass for clean teardown. Use close() only when you intentionally want to close one tab (like a popup window) while keeping others open. In my framework, @AfterMethod calls driver.quit(), and I have a safety check: if (driver != null) { driver.quit(); } to prevent NullPointerException if the browser crashed during the test.
Q: How do you run tests in headless mode?
A: Headless mode runs the browser without a visible UI — faster and uses fewer resources. For Chrome: ChromeOptions options = new ChromeOptions(); options.addArguments("--headless=new"); — use "--headless=new" (Chrome 112+, more reliable than old --headless). For Firefox: options.addArguments("--headless"). In my framework, this is configurable: if (config.getProperty("headless").equals("true")) { options.addArguments("--headless=new"); }. CI/CD always runs headless since there is no display. Important: set a window size in headless mode — options.addArguments("--window-size=1920,1080") — because the default is often small and elements may not be visible. Some visual tests may behave differently headless, so I run critical UI validation tests in headed mode.
Q: What is Selenium Grid? How does RemoteWebDriver work?
A: Selenium Grid runs tests on multiple machines and browsers in parallel. Hub-Node architecture: Hub receives test requests and distributes to Nodes. Nodes have browsers installed and execute the tests. Selenium 4 redesigned Grid with Router, Distributor, Session Map, and Node components. To use it, replace local driver with RemoteWebDriver: ChromeOptions options = new ChromeOptions(); WebDriver driver = new RemoteWebDriver(new URL("http://grid-hub:4444"), options). The Grid Hub routes the request to a Node with Chrome. In my framework, I switch with a config property: if (execution.equals("remote")) { driver = new RemoteWebDriver(hubUrl, options); } else { driver = new ChromeDriver(options); }. For Docker-based Grid: docker run -d selenium/standalone-chrome and connect to port 4444. This is how I run tests in CI without installing browsers on the build server.
Q: How do you handle file upload and download in Selenium?
A: Upload: for standard <input type="file">, use sendKeys with the absolute path — driver.findElement(By.id("fileUpload")).sendKeys("/path/to/file.pdf"). Do not click the input first. For custom uploaders (dropzones), make the hidden input visible with JavascriptExecutor and then use sendKeys. Download: set Chrome preferences to auto-download to a specific directory: Map<String, Object> prefs = new HashMap<>(); prefs.put("download.default_directory", downloadPath); prefs.put("download.prompt_for_download", false); options.setExperimentalOption("prefs", prefs). Then verify the file exists: wait until new File(downloadPath + "/report.pdf").exists(). In my framework, I have a FileUtil class with methods uploadFile(By locator, String filePath) and waitForDownload(String fileName, int timeoutSeconds).
Key Point: Selenium answers must include project context. "In my framework, I handle this by..." is the phrase that gets you hired.