LTIMindtree SDET Interview Questions: Technical & Advanced Automation Rounds
Two-round SDET interview questions with short, practical answers: Selenium, Playwright, TestNG, JDBC, SQL, Jenkins, Git, flaky tests and CI debugging.
Round 1: Technical
1. Self intro and framework architecture overview.
Keep the intro under 2 minutes: present role and stack, past frameworks and impact, why this role next. Then walk the framework top-down:
- Tests: TestNG (or Cucumber) classes that only describe behaviour.
- Page layer: Page Objects that hold locators and actions.
- Core: driver factory (ThreadLocal), config reader, waits, logging.
- Data: JSON/Excel/API-created test data.
- Reporting: Extent/Allure, screenshots on failure.
- Execution: Maven + Jenkins, parallel runs, environment chosen by a parameter.
2. Explain the Page Object Model (POM) design pattern used.
One class per page or reusable component. The class owns the locators and exposes actions; tests never touch locators directly.
public class LoginPage {
private final WebDriver driver;
private final By user = By.id("username");
private final By pass = By.id("password");
public LoginPage(WebDriver driver) { this.driver = driver; }
public HomePage login(String u, String p) {
driver.findElement(user).sendKeys(u);
driver.findElement(pass).sendKeys(p);
driver.findElement(By.id("submit")).click();
return new HomePage(driver);
}
}
Benefits: a UI change is fixed in one place, tests read like business flows, and pages are reusable. Methods that navigate return the next page object (fluent style).
3. Remove all vowels from a string, e.g. automation -> tmtn.
static String removeVowels(String s) {
return s.replaceAll("(?i)[aeiou]", "");
}
// "automation" -> "tmtn"
Without regex, loop and append only the characters that are not in "aeiouAEIOU" using a StringBuilder.
4. How do you handle StaleElementReferenceException in Selenium?
It means the element was found earlier, but the DOM was refreshed and the reference is no longer attached. Fixes:
- Re-locate the element instead of holding on to a stored
WebElement. - Wait for the refreshed element:
ExpectedConditions.refreshed(...). - Retry the action once or twice in a small helper.
new WebDriverWait(driver, Duration.ofSeconds(10))
.until(ExpectedConditions.refreshed(
ExpectedConditions.elementToBeClickable(By.id("save"))))
.click();
Prefer By locators in page objects, so every call finds a fresh element.
5. How do you interact with dynamic web tables in WebDriver?
Never hard-code row or column indexes. Locate rows, then find the cell by its text.
List<WebElement> rows = driver.findElements(By.xpath("//table[@id='emp']//tbody/tr"));
for (WebElement row : rows) {
if (row.findElement(By.xpath("./td[1]")).getText().equals("John")) {
row.findElement(By.xpath("./td[4]//button")).click();
break;
}
}
Or use one dynamic XPath: //tr[td[text()='John']]/td[4]. For paginated tables, loop until the “Next” button is disabled.
6. How do you switch across nested frames and back to the parent?
driver.switchTo().frame("outer"); // by name/id, index or WebElement
driver.switchTo().frame("inner"); // frame inside the frame
// ... work inside inner frame ...
driver.switchTo().parentFrame(); // one level up (back to outer)
driver.switchTo().defaultContent(); // all the way to the main page
Switch only one level at a time. Elements in the main page cannot be found while the driver is inside a frame.
7. How do you extract a JWT auth token from response headers?
With REST Assured:
Response res = given().body(creds).post("/login");
String token = res.getHeader("Authorization"); // e.g. "Bearer eyJhbGci..."
// if it is in the body instead: res.jsonPath().getString("token")
given().header("Authorization", token).get("/profile").then().statusCode(200);
Do it once in a setup method, store it, and reuse it. Refresh it when it expires.
8. Playwright auto-waiting vs Selenium explicit waits.
- Playwright: every action (
click,fill) automatically waits for the element to be attached, visible, stable, enabled and not covered. Assertions likeexpect(locator).toBeVisible()retry until timeout. - Selenium:
findElementdoes not wait by default. You add implicit or explicit waits yourself, per condition.
Result: fewer waits and less flakiness in Playwright. In Selenium, use WebDriverWait with a specific condition and never Thread.sleep.
9. How do you execute DB queries via JDBC and verify row counts?
String sql = "SELECT COUNT(*) FROM orders WHERE status = ?";
try (Connection con = DriverManager.getConnection(url, user, pwd);
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setString(1, "PLACED");
try (ResultSet rs = ps.executeQuery()) {
rs.next();
Assert.assertEquals(rs.getInt(1), expectedCount);
}
}
Use PreparedStatement (no SQL injection), try-with-resources (no leaked connections), and keep credentials in config, not in code.
10. SQL query to count employees grouped by department.
SELECT department, COUNT(*) AS employee_count
FROM employee
GROUP BY department;
-- only departments with more than 5 people: add HAVING COUNT(*) > 5
11. Strategy for flaky tests failing intermittently in the pipeline.
- Find the pattern: which tests, which environment, what time. Check history in the report.
- Classify the cause: timing, shared data, order dependence, environment, or a real product bug.
- Fix the root cause: proper waits, stable locators, isolated data, no shared state.
- Contain: quarantine into a separate suite so it does not block the pipeline, and track it as a ticket.
- Retry is the last layer, not the fix. Always log retried tests so they stay visible.
12. Configuring TestNG IRetryAnalyzer for automatic retries.
public class Retry implements IRetryAnalyzer {
private int count = 0;
private static final int MAX = 2;
@Override
public boolean retry(ITestResult result) {
return count++ < MAX;
}
}
Use it per test:
@Test(retryAnalyzer = Retry.class)
public void checkout() { ... }
To apply it to all tests, implement IAnnotationTransformer and call annotation.setRetryAnalyzer(Retry.class), then register it as a listener.
Round 2: Advanced Automation
1. End-to-end execution lifecycle when a Jenkins build gets triggered.
- Trigger: commit/webhook, schedule, or manual build.
- Checkout: Jenkins pulls the code from Git.
- Build:
mvn clean compileand dependency resolution. - Test:
mvn test -Dsuite=regression -Denv=qa(parallel, on agents or Docker). - Reports: publish TestNG/Allure/Extent results and archive screenshots.
- Notify: email or Slack with pass/fail summary.
- Cleanup: clean the workspace and stop containers.
2. Find the maximum element in an array, e.g. [14, 82, 5, 63] -> 82.
static int max(int[] a) {
int max = a[0];
for (int n : a) if (n > max) max = n;
return max;
}
// or: Arrays.stream(a).max().getAsInt();
One pass, O(n) time, O(1) space.
3. Handling multiple windows and tabs using driver.getWindowHandles().
String parent = driver.getWindowHandle();
for (String handle : driver.getWindowHandles()) {
if (!handle.equals(parent)) {
driver.switchTo().window(handle);
break;
}
}
// ... work in the new window ...
driver.close();
driver.switchTo().window(parent);
Selenium 4 also offers driver.switchTo().newWindow(WindowType.TAB).
4. Ensuring ThreadLocal safety during parallel test runs.
Keep one driver per thread, never a shared static driver.
public class DriverManager {
private static final ThreadLocal<WebDriver> DRIVER = new ThreadLocal<>();
public static void set(WebDriver d) { DRIVER.set(d); }
public static WebDriver get() { return DRIVER.get(); }
public static void quit() {
DRIVER.get().quit();
DRIVER.remove(); // avoid leaks in thread pools
}
}
Also: no shared static state (reports, test data, page objects), create the driver in @BeforeMethod, quit in @AfterMethod, and use parallel="methods" with thread-count in testng.xml.
5. Capturing screenshots on test failure using TestNG listeners.
public class FailureListener implements ITestListener {
@Override
public void onTestFailure(ITestResult result) {
File src = ((TakesScreenshot) DriverManager.get()).getScreenshotAs(OutputType.FILE);
Files.copy(src.toPath(), Path.of("screenshots", result.getName() + ".png"),
StandardCopyOption.REPLACE_EXISTING);
}
}
Register it with @Listeners(FailureListener.class) or in testng.xml. Get the driver from the ThreadLocal so it works in parallel runs, and attach the image to the report.
6. Mocking 404 API responses in Playwright route handlers.
await page.route('**/api/users/*', route =>
route.fulfill({
status: 404,
contentType: 'application/json',
body: JSON.stringify({ error: 'User not found' }),
})
);
await page.goto('/profile');
await expect(page.getByText('User not found')).toBeVisible();
This tests the UI’s error handling without needing the backend to fail. Use route.continue() for requests you don’t want to mock.
7. Test data isolation strategies for QA vs Staging environments.
- Config per environment: URLs, credentials and DB details in separate files, selected by
-Denv=qa. - Create data per test through API or DB, with unique values (timestamp/UUID) so parallel runs don’t collide.
- Never run destructive tests on Staging. Keep read-only or clearly tagged test accounts there.
- Secrets in the CI vault, not in the repo.
- Clean up what the test created, so the next run starts clean.
8. Debugging scripts that pass locally but fail on CI.
Check, in this order:
- Logs, screenshots and video from the failed run.
- Headless vs headed: set the window size explicitly (
--window-size=1920,1080). - Environment differences: browser/driver versions, OS, timezone, locale, fonts.
- Timing: CI is slower, so replace sleeps with condition-based waits.
- Data and config: missing env variables, different test data, network or firewall limits.
- Parallelism: shared state that only breaks under threads.
Reproduce it by running the same Docker image locally.
9. Handling requirement changes mid-sprint effectively.
Understand the change and its impact first. Talk to the PO and dev about scope, then update the acceptance criteria and affected test cases. Re-prioritise: automate the stable parts first, and flag risk if the sprint capacity doesn’t fit. Keep tests modular so a change touches one place. Communicate early, and record the decision.
10. Resolving Git rebase conflicts during active sprint merges.
git fetch origin
git rebase origin/main
# conflict: edit the files, keep the correct changes
git add <file>
git rebase --continue
# if it goes wrong: git rebase --abort
git push --force-with-lease
Rebase small and often to keep conflicts small. Use --force-with-lease, never plain --force. If the conflict is in logic you don’t own, ask that developer. Run the tests again after resolving.
11. Cleaning up test DB records after script execution.
- Delete in
@AfterMethod/@AfterClass, children before parents (foreign keys). - Delete only what the test created, by its unique ID or tag. Never
DELETEwithout aWHERE. - Prefer the application’s API or a rollback-able transaction where possible.
- Run cleanup in a
finally/teardown so it still runs when the test fails. - Never clean a shared or production-like database without guardrails.
@AfterMethod(alwaysRun = true)
public void cleanup() {
jdbc.update("DELETE FROM orders WHERE created_by = ?", RUN_ID);
}