These questions test your real-world problem-solving. Any engineer can write a test — the senior engineer is the one who debugs it when it fails at 2 AM on the CI server. Prepare specific stories about problems you solved.
Q: A test passes locally but fails in CI. How do you debug?
A: This is the #1 real-world issue. My checklist: (1) Headless mode — CI often runs headless. Run locally in headless mode to reproduce: options.addArguments("--headless=new"). (2) Screen resolution — CI servers have small default resolution. Elements may not be visible. Add: options.addArguments("--window-size=1920,1080"). (3) Timing — CI servers are slower. Check if the failure is a TimeoutException and increase the wait or add a specific explicit wait. (4) Test data — CI might use a different database or environment. Verify the test data exists. (5) Screenshots and logs — my framework captures both on failure. Check the Allure report. (6) Browser version — CI might have a different Chrome version. Check compatibility. (7) Test isolation — the test might depend on state from another test. Run it in isolation on CI. (8) Concurrency — if parallel execution, check for shared state issues. Most CI failures come down to timing or resolution. Fix these and 80% of the problems disappear.
Q: How do you handle flaky tests?
A: Flaky tests pass sometimes, fail sometimes with the same code. My approach: (1) Short-term — implement IRetryAnalyzer to auto-retry 2 times. Prevents false alarms while I investigate. (2) Identify root cause — check screenshots and logs. Common causes: timing (element not loaded — add explicit wait), environment instability (network delays), test data conflicts (two parallel tests using same data — use Faker for unique data), test order dependency (shared state — make tests independent), dynamic elements (locator broke — fix it). (3) Fix properly — do not just add retries and walk away. Replace Thread.sleep with explicit waits. Use unique data per run. Make tests independent. (4) Track flaky tests — I maintain a list of tests that needed retries. If a test retries consistently, it gets priority investigation. (5) Prevention — code reviews catch anti-patterns: Thread.sleep, hardcoded waits, shared state, test dependencies.
Q: How do you handle StaleElementReferenceException in a real project?
A: This happens when the DOM changes between finding an element and interacting with it. Real scenarios where I have encountered it: (1) AJAX refresh — a table reloads after filtering, and my reference to a row is stale. Fix: store the By locator, not the WebElement, and re-find before each interaction. (2) SPA navigation — React re-renders the component, creating a new DOM node. Fix: wait for the component to stabilize using a custom ExpectedCondition. (3) Pagination — clicking "next page" replaces table rows. Fix: re-find elements after each page change. My BasePage handles this by design: all interaction methods accept By locators and call findElement() fresh each time. I also have a retry wrapper: retryOnStale(() -> driver.findElement(locator).click(), 3) that catches StaleElementReferenceException and retries up to 3 times.
Q: ElementClickInterceptedException — what is it and how do you fix it?
A: This means another element is covering the one you want to click — a loading spinner, a cookie consent banner, a sticky header, or a modal overlay. Debugging: (1) Check the screenshot — what is covering the element? (2) Wait for the overlay to disappear: wait.until(EC.invisibilityOfElementLocated(By.cssSelector(".loading-spinner"))). (3) Scroll the element to center: js.executeScript("arguments[0].scrollIntoView({block:'center'});", element) — centering avoids sticky headers covering it. (4) Dismiss blocking elements — close the cookie banner, dismiss the modal. (5) Last resort: js.executeScript("arguments[0].click();", element) — JavaScript click bypasses the visibility check. But I use this cautiously because JS click can mask real UI bugs. In my framework, my click() method automatically scrolls to center before clicking, which prevents most interception issues.
Q: How do you debug a test that fails with TimeoutException?
A: TimeoutException means an explicit wait condition was not met within the timeout. Debugging: (1) Check what the wait was waiting for — visibilityOfElementLocated? elementToBeClickable? textToBePresentInElement? (2) Screenshot — was the element there? Was it hidden? Was it in a different state? (3) Check the page — did navigation happen? Is the URL correct? Did the page load at all? (4) Check if the element is inside an iframe — a common miss. (5) Check if the locator is correct — verify it in DevTools console. (6) Check if the condition is right — maybe the element IS present but NOT visible (wrong ExpectedCondition). (7) Increase timeout temporarily to see if it is just a speed issue. If increasing the timeout fixes it, the root cause is environment performance, not a bug. In CI, I set timeouts higher than local (15s vs 10s) because CI servers are shared and slower.
Q: How do you handle tests that work with one browser but fail with another?
A: Cross-browser differences are real. Common issues: (1) CSS rendering — element positions differ between Chrome and Firefox. Locators based on position may break. Use stable attributes instead. (2) Event handling — some browsers handle keyboard events differently. Use Actions class for complex interactions. (3) Timing — Firefox is often slower than Chrome for rendering. Increase waits or use browser-specific timeouts. (4) JavaScript execution — some JS features are browser-specific. Avoid browser-dependent JS in JavascriptExecutor. (5) File upload/download — path handling differs across browsers and OS. My debugging approach: run the failing test locally with the problematic browser, compare screenshots with the passing browser, check if the locator is valid in both (DevTools in each browser), and check for browser-specific CSS behavior. Most cross-browser issues are locator or timing problems.
Q: How do you handle CAPTCHA, OTP, and third-party integrations in tests?
A: CAPTCHA: do not automate it — it is designed to prevent automation. Ask developers to disable CAPTCHA in the test environment using a config flag. For reCAPTCHA, use Google test keys that always pass. OTP: three approaches — (1) ask developers to use a fixed OTP in the test environment (like "123456"), (2) read OTP from the test email using an email API (like Mailinator or Guerrilla Mail), (3) read OTP from the database directly if it is stored there. Third-party payments (Razorpay, Stripe): use the sandbox/test mode that these services provide. Test credit card numbers are publicly available. The principle: never try to hack around security measures. Work with the development team to create test-environment bypasses. This is the correct answer in every interview — it shows you understand the collaboration between QA and dev.
Q: What is your approach when you encounter a bug during automation?
A: First, I verify it is a real bug, not a test issue. (1) Reproduce manually — open the browser, follow the same steps. If it fails manually, it is an application bug. (2) Check the test — is the locator correct? Is the test data valid? Is the expected result accurate? If the test is wrong, fix the test. (3) If it is a real bug: capture evidence — screenshot, URL, test data used, expected vs actual result. (4) Check the severity — is it a blocker? Does it affect other tests? (5) Log a bug ticket with: steps to reproduce, expected result, actual result, environment, screenshots, and severity. (6) Skip the test temporarily with @Test(enabled = false) and a comment linking to the bug ticket. (7) Re-enable the test when the bug is fixed. I never silently skip or delete a test because it found a real bug. The bug gets tracked and the test gets re-enabled after the fix.
Key Point: Debugging skills separate experienced automation engineers from beginners. Prepare 2-3 detailed stories about real problems you solved.