Automation Testing Programming Interview Questions
A practical question bank for QA Automation Engineer and SDET interviews, with Java, Selenium, TypeScript, Playwright, API testing, SQL, and framework engineering examples.
1. Java Core Programming
Practice strings, arrays, maps, algorithms, and object-oriented programming.
1. Reverse a string without using StringBuilder.reverse()
Difficulty: Beginner
Solution:
public class ReverseString {
public static String reverse(String input) {
StringBuilder result = new StringBuilder();
for (int i = input.length() - 1; i >= 0; i--) {
result.append(input.charAt(i));
}
return result.toString();
}
public static void main(String[] args) {
System.out.println(reverse("Playwright"));
}
}
Output: thgirwyalP
Explanation: Iterate from the last character to the first and append each character. Time complexity is O(n); auxiliary space is O(n).
2. Check whether a string is a palindrome
Difficulty: Beginner
public static boolean isPalindrome(String value) {
String normalized = value.replaceAll("[^A-Za-z0-9]", "")
.toLowerCase();
int left = 0;
int right = normalized.length() - 1;
while (left < right) {
if (normalized.charAt(left) != normalized.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
public static void main(String[] args) {
System.out.println(isPalindrome("Never odd or even"));
}
Output: true
Explanation: Normalize case and remove punctuation, then compare characters from both ends. Time complexity is O(n).
3. Find duplicate numbers in an integer array
Difficulty: Beginner–Intermediate
import java.util.*;
public static Set<Integer> findDuplicates(int[] numbers) {
Set<Integer> seen = new HashSet<>();
Set<Integer> duplicates = new LinkedHashSet<>();
for (int number : numbers) {
if (!seen.add(number)) {
duplicates.add(number);
}
}
return duplicates;
}
// Example:
System.out.println(findDuplicates(new int[]{2, 4, 2, 5, 4, 7}));
Output: [2, 4]
Explanation: Set.add() returns false if the value already exists. Expected time complexity is O(n), with O(n) extra space.
4. Move all zeros to the right while preserving other elements' order
Difficulty: Beginner–Intermediate
import java.util.Arrays;
public static void moveZeros(int[] values) {
int write = 0;
for (int value : values) {
if (value != 0) {
values[write++] = value;
}
}
while (write < values.length) {
values[write++] = 0;
}
}
public static void main(String[] args) {
int[] values = {1, 0, 3, 0, 4, 5};
moveZeros(values);
System.out.println(Arrays.toString(values));
}
Output: [1, 3, 4, 5, 0, 0]
Explanation: Copy non-zero values forward, then fill the remaining positions with zero. Time O(n), extra space O(1). Sorting is not suitable because it can change the order of non-zero elements.
5. Find the second-largest distinct number without sorting
Difficulty: Intermediate
public static int secondLargest(int[] values) {
Integer largest = null;
Integer second = null;
for (int value : values) {
if (largest == null || value > largest) {
if (largest == null || value != largest) {
second = largest;
largest = value;
}
} else if (value != largest &&
(second == null || value > second)) {
second = value;
}
}
if (second == null) {
throw new IllegalArgumentException(
"At least two distinct values are required");
}
return second;
}
// Example: secondLargest(new int[]{10, 5, 8, 10, 7}) == 8
Output for the example: 8
Explanation: Track the largest and second-largest distinct values in one pass. Time O(n), extra space O(1).
6. Count how many times each character appears in a string
Difficulty: Intermediate
import java.util.*;
public static Map<Character, Integer> characterCounts(String input) {
Map<Character, Integer> counts = new LinkedHashMap<>();
for (char ch : input.toCharArray()) {
counts.put(ch, counts.getOrDefault(ch, 0) + 1);
}
return counts;
}
// characterCounts("automation")
Example counts: a=2, u=1, t=2, o=2, m=1, i=1, n=1
Explanation: A map stores each character and its running count. This version is case-sensitive and counts spaces if present. Time O(n).
7. Check whether two strings are anagrams
Difficulty: Intermediate
import java.util.Arrays;
public static boolean areAnagrams(String first, String second) {
char[] a = first.replaceAll("\\s+", "")
.toLowerCase().toCharArray();
char[] b = second.replaceAll("\\s+", "")
.toLowerCase().toCharArray();
if (a.length != b.length) return false;
Arrays.sort(a);
Arrays.sort(b);
return Arrays.equals(a, b);
}
// areAnagrams("listen", "silent") == true
Explanation: After normalization, anagrams contain the same characters with the same frequencies. Sorting makes comparison straightforward. Time O(n log n).
8. Find the first non-repeating character in a string
Difficulty: Intermediate
import java.util.*;
public static Character firstUnique(String input) {
Map<Character, Integer> counts = new LinkedHashMap<>();
for (char ch : input.toCharArray()) {
counts.put(ch, counts.getOrDefault(ch, 0) + 1);
}
for (Map.Entry<Character, Integer> entry : counts.entrySet()) {
if (entry.getValue() == 1) return entry.getKey();
}
return null;
}
// firstUnique("swiss") returns 'w'
Explanation: A LinkedHashMap preserves insertion order, allowing a second pass to return the first character whose count is one. Time O(n), space O(k) for distinct characters.
9. Generate the Fibonacci sequence iteratively
Difficulty: Beginner
public static void printFibonacci(int count) {
long first = 0;
long second = 1;
for (int i = 0; i < count; i++) {
System.out.print(first + (i + 1 == count ? "" : " "));
long next = first + second;
first = second;
second = next;
}
}
// printFibonacci(7)
Output: 0 1 1 2 3 5 8
Explanation: Each term is the sum of the previous two. Time O(n), extra space O(1). The long type still has a finite range.
10. Find the missing number from the range 1 to n
Difficulty: Intermediate
public static int missingNumber(int[] values, int n) {
long expected = (long) n * (n + 1) / 2;
long actual = 0;
for (int value : values) {
actual += value;
}
return Math.toIntExact(expected - actual);
}
// missingNumber(new int[]{1, 2, 4, 5}, 5) returns 3
Explanation: Assumes values contain distinct integers from 1 through n with exactly one missing. The formula computes the expected total. Time O(n), extra space O(1).
2. TypeScript Core Programming
Practice language fundamentals used in Playwright tests and automation utilities.
11. Reverse a string in TypeScript
Difficulty: Beginner
function reverseString(value: string): string {
return Array.from(value).reverse().join("");
}
console.log(reverseString("Playwright"));
Output: thgirwyalP
Explanation: Array.from handles Unicode code points better than splitting into UTF-16 code units, although complex emoji sequences may still need grapheme-aware processing. Time O(n).
12. Remove duplicate values from an array
Difficulty: Beginner
function uniqueNumbers(values: number[]): number[] {
return [...new Set(values)];
}
console.log(uniqueNumbers([1, 2, 2, 3, 1, 4]));
Output: [1, 2, 3, 4]
Explanation: A Set stores unique values and preserves insertion order for iteration. Expected time O(n) and extra space O(n).
13. Count the frequency of each word
Difficulty: Intermediate
function wordFrequency(sentence: string): Map<string, number> {
const counts = new Map<string, number>();
const words = sentence.toLowerCase().match(/[a-z0-9']+/g) ?? [];
for (const word of words) {
counts.set(word, (counts.get(word) ?? 0) + 1);
}
return counts;
}
console.log([...wordFrequency("Test well, test often!")]);
Explanation: The regular expression extracts word-like tokens, normalizes case, and counts occurrences in a Map. Adjust tokenization for languages or punctuation rules your application needs.
14. Explain async/await and handle errors in TypeScript
Difficulty: Intermediate
async function fetchUserName(url: string): Promise<string> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data: unknown = await response.json();
if (
typeof data === "object" &&
data !== null &&
"name" in data &&
typeof data.name === "string"
) {
return data.name;
}
throw new Error("Response did not contain a string name");
}
Explanation: An async function returns a Promise. Await pauses that function until the Promise settles. Check HTTP status explicitly; fetch does not reject solely because a server returns 404 or 500. Treat external JSON as unknown until validated.
15. Group an array of test results by status
Difficulty: Intermediate
type TestResult = {
name: string;
status: "passed" | "failed" | "skipped";
};
function groupByStatus(results: TestResult[]) {
return results.reduce<Record<string, TestResult[]>>(
(groups, result) => {
(groups[result.status] ??= []).push(result);
return groups;
},
{}
);
}
const results: TestResult[] = [
{ name: "login", status: "passed" },
{ name: "checkout", status: "failed" },
{ name: "profile", status: "passed" }
];
console.log(groupByStatus(results));
Explanation: This utility groups test results by their status, which can be useful for building a simple test summary. The union type restricts status values at compile time.
3. Selenium WebDriver with Java
16. Write an explicit wait for a clickable element
Difficulty: Beginner–Intermediate
import java.time.Duration;
import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
WebDriverWait wait = new WebDriverWait(
driver, Duration.ofSeconds(10));
WebElement submit = wait.until(
ExpectedConditions.elementToBeClickable(
By.id("submit")
)
);
submit.click();
Explanation: An explicit wait polls until the specified condition succeeds or times out. Do not mix implicit and explicit waits casually, because combined wait behavior can be difficult to predict.
17. Find all broken or non-success HTTP links on a page
Difficulty: Advanced
import java.net.URI;
import java.net.http.*;
import java.time.Duration;
import java.util.List;
import org.openqa.selenium.*;
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
List<WebElement> links =
driver.findElements(By.cssSelector("a[href]"));
for (WebElement link : links) {
String href = link.getAttribute("href");
if (href == null || href.isBlank()) continue;
try {
URI uri = URI.create(href);
if (!List.of("http", "https").contains(uri.getScheme())) continue;
HttpRequest request = HttpRequest.newBuilder(uri)
.timeout(Duration.ofSeconds(10))
.GET()
.build();
HttpResponse<Void> response = client.send(
request, HttpResponse.BodyHandlers.discarding());
if (response.statusCode() >= 400) {
System.out.println("Potential broken link: " + href
+ " status=" + response.statusCode());
}
} catch (Exception ex) {
System.out.println("Could not verify: " + href
+ " reason=" + ex.getMessage());
}
}
Explanation: This is a starting point, not a universal broken-link oracle. Some servers reject automated requests, require authentication, or treat GET differently from HEAD. Use controlled test environments and distinguish inaccessible links from confirmed broken links.
18. Switch to a newly opened browser window
Difficulty: Intermediate
import java.util.Set;
import org.openqa.selenium.*;
String originalWindow = driver.getWindowHandle();
driver.findElement(By.id("open-window")).click();
new WebDriverWait(driver, Duration.ofSeconds(10))
.until(d -> d.getWindowHandles().size() > 1);
Set<String> handles = driver.getWindowHandles();
for (String handle : handles) {
if (!handle.equals(originalWindow)) {
driver.switchTo().window(handle);
break;
}
}
System.out.println(driver.getTitle());
// When finished, close the child and return to parent:
driver.close();
driver.switchTo().window(originalWindow);
Explanation: Wait for the second window instead of assuming it opens instantly. Close only the child window before switching back to the original one.
19. Capture a screenshot when a Selenium test fails
Difficulty: Intermediate
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import org.openqa.selenium.*;
import org.openqa.selenium.io.FileHandler;
public static void captureScreenshot(
WebDriver driver, Path destination) throws Exception {
Files.createDirectories(destination.getParent());
File source = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
FileHandler.copy(source, destination.toFile());
}
// Example:
// captureScreenshot(driver, Path.of("artifacts", "failure.png"));
Explanation: This helper creates the destination directory and saves a screenshot. Call it from a TestNG listener or failure-handling hook. Ensure the driver is still available when the failure is processed.
20. Create a reusable Selenium Page Object
Difficulty: Intermediate
import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.*;
public class LoginPage {
private final WebDriver driver;
private final WebDriverWait wait;
private final By email = By.id("email");
private final By password = By.id("password");
private final By loginButton = By.id("login");
public LoginPage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(
driver, java.time.Duration.ofSeconds(10));
}
public void login(String userEmail, String userPassword) {
wait.until(ExpectedConditions.visibilityOfElementLocated(email))
.sendKeys(userEmail);
driver.findElement(password).sendKeys(userPassword);
wait.until(ExpectedConditions.elementToBeClickable(loginButton))
.click();
}
}
Explanation: The page object encapsulates locators and user actions. Tests should verify outcomes, such as a dashboard heading or URL, rather than only checking that the click method returned.
4. Playwright with TypeScript
21. Write a Playwright test using a role locator and assertion
Difficulty: Beginner
import { test, expect } from "@playwright/test";
test("user can open the home page", async ({ page }) => {
await page.goto("https://example.com");
await expect(
page.getByRole("heading", { name: "Example Domain" })
).toBeVisible();
});
Explanation: Role locators reflect how users and assistive technology identify elements. Web-first assertions automatically retry until the condition passes or the assertion times out.
22. Create a custom Playwright fixture
Difficulty: Advanced
import { test as base, expect } from "@playwright/test";
type Fixtures = {
appUrl: string;
};
export const test = base.extend<Fixtures>({
appUrl: async ({}, use) => {
await use(process.env.APP_URL ?? "http://127.0.0.1:3000");
}
});
export { expect };
// Usage in another test file:
// import { test, expect } from "./fixtures";
// test("app loads", async ({ page, appUrl }) => {
// await page.goto(appUrl);
// await expect(page.locator("body")).toBeVisible();
// });
Explanation: Fixtures centralize shared setup and dependencies. In real projects, validate required environment variables and avoid embedding secrets in source code.
23. Handle a popup in Playwright
Difficulty: Intermediate
import { test, expect } from "@playwright/test";
test("opens a details popup", async ({ page }) => {
await page.goto("https://example.com");
const popupPromise = page.waitForEvent("popup");
await page.getByRole("link", { name: "Open popup" }).click();
const popup = await popupPromise;
await expect(popup).toHaveURL(/details/);
});
Explanation: Start waiting for the popup before clicking. Replace the example link and URL pattern with elements and routes from your application.
24. Mock an API response in a Playwright UI test
Difficulty: Advanced
import { test, expect } from "@playwright/test";
test("renders mocked products", async ({ page }) => {
await page.route("**/api/products", async route => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify([
{ id: 1, name: "Laptop" }
])
});
});
await page.goto("http://127.0.0.1:3000/products");
await expect(page.getByText("Laptop")).toBeVisible();
});
Explanation: Route interception isolates the UI test from a live API response. The route must match the request made by the application, and it should be registered before that request occurs.
25. Write a reusable Playwright Page Object
Difficulty: Intermediate
import { type Locator, type Page } from "@playwright/test";
export class LoginPage {
readonly page: Page;
readonly email: Locator;
readonly password: Locator;
readonly submit: Locator;
constructor(page: Page) {
this.page = page;
this.email = page.getByLabel("Email");
this.password = page.getByLabel("Password");
this.submit = page.getByRole("button", { name: "Login" });
}
async open() {
await this.page.goto("/login");
}
async login(email: string, password: string) {
await this.email.fill(email);
await this.password.fill(password);
await this.submit.click();
}
}
Explanation: A page object keeps selectors and actions in one place. Configure baseURL in Playwright so relative URLs such as /login resolve correctly.
5. API Testing Programming
26. Validate an HTTP status and JSON response with Playwright API testing
Difficulty: Intermediate
import { test, expect } from "@playwright/test";
test("API returns a user", async ({ request }) => {
const response = await request.get(
"https://jsonplaceholder.typicode.com/users/1"
);
expect(response.status()).toBe(200);
const body: unknown = await response.json();
expect(body).toMatchObject({
id: 1,
username: expect.any(String)
});
});
Explanation: The request fixture sends HTTP requests without opening a browser page. In production tests, use your test environment and validate headers, schema, business rules, and negative cases too.
27. Send a POST request with a JSON body
Difficulty: Intermediate
import { test, expect } from "@playwright/test";
test("create a user", async ({ request }) => {
const response = await request.post(
"https://api.example.com/users",
{
data: {
name: "Alex",
role: "tester"
},
headers: {
"Content-Type": "application/json"
}
}
);
expect(response.status()).toBe(201);
const body = await response.json();
expect(body.name).toBe("Alex");
});
Explanation: This illustrates a typical create operation. The endpoint must support POST and return the documented status and response shape; replace the placeholder domain with a test API.
28. What should an API automation test validate beyond status code?
Difficulty: Intermediate–Advanced
Answer: Validate response body fields and types, headers, schema, business rules, authentication and authorization, error messages, response time against an agreed threshold, and side effects where relevant.
const response = await request.get("/api/orders/123");
expect(response.status()).toBe(200);
expect(response.headers()["content-type"]).toContain("application/json");
const order = await response.json();
expect(order).toMatchObject({
id: "123",
status: "CONFIRMED"
});
expect(typeof order.total).toBe("number");
Explanation: A 200 response alone does not establish correctness. Assertions should reflect the API contract and expected business behavior.
6. SQL for Automation Testers
29. Find duplicate email addresses in a users table
Difficulty: Beginner–Intermediate
SELECT email, COUNT(*) AS occurrences
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
Explanation: GROUP BY groups identical email values; HAVING filters groups with more than one row. If email comparison should ignore case or surrounding spaces, normalize values according to the database's rules.
30. Find employees earning more than their department's average salary
Difficulty: Intermediate
SELECT e.employee_id, e.name, e.department_id, e.salary
FROM employees e
JOIN (
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
) d
ON d.department_id = e.department_id
WHERE e.salary > d.avg_salary;
Explanation: The subquery calculates average salary by department, and the outer query returns employees whose salary is above that average. NULL department IDs do not match using ordinary equality.
31. Find records present in one table but missing from another
Difficulty: Intermediate
SELECT a.customer_id
FROM source_customers a
LEFT JOIN target_customers b
ON b.customer_id = a.customer_id
WHERE b.customer_id IS NULL;
Explanation: This is useful when validating ETL or data migration. It identifies source customer IDs that have no matching target record. Compare additional columns and account for duplicates when validating complete datasets.
7. Framework Utilities and SDET Design
32. How should test configuration and secrets be managed?
Difficulty: Intermediate
Answer: Keep non-secret environment-specific settings in configuration and inject secrets through environment variables or a dedicated secrets manager. Avoid committing passwords, tokens, or authenticated storage files to source control.
// TypeScript example
const baseURL = process.env.BASE_URL;
if (!baseURL) {
throw new Error("BASE_URL environment variable is required");
}
export const config = { baseURL };
Explanation: Configuration should be validated early so a missing value fails clearly rather than causing confusing downstream test failures.
33. How do you make test data unique and reduce parallel-test conflicts?
Difficulty: Advanced
import { randomUUID } from "node:crypto";
export function createTestEmail(): string {
return `qa-${randomUUID()}@example.test`;
}
Explanation: Unique identifiers reduce collisions between parallel tests. Use a reserved test domain or a controlled test environment, and clean up created records where possible. Uniqueness alone does not replace test isolation or cleanup.
34. What is the difference between unit, integration, and end-to-end tests?
Difficulty: Beginner–Intermediate
- Unit tests: Validate a small unit of code in isolation, usually quickly.
- Integration tests: Verify interactions between components, such as an application and a database or service.
- End-to-end tests: Validate complete user workflows across multiple application layers.
Example: Test a date-formatting utility with a unit test, verify an order service writes to a database with an integration test, and place an order through the browser with an end-to-end test.
Interview tip: Use the test level that gives reliable feedback at the lowest practical cost; not every business rule needs a full browser test.
35. How do you design a reliable automation framework?
Difficulty: Advanced
Answer: A maintainable framework separates test scenarios, page objects or screen abstractions, test data, configuration, reusable API clients, reporting, and environment setup. It also defines conventions for assertions, logging, retries, parallelism, and cleanup.
A practical design checklist:
- Keep tests independent and deterministic.
- Use accessible locators and condition-based waits.
- Centralize configuration without hardcoding secrets.
- Capture useful artifacts on failures.
- Run a fast smoke suite and a broader regression suite in CI.
- Track flaky tests and fix their root causes instead of hiding them with retries.
Explanation: A good framework optimizes maintainability, diagnosability, execution speed, and trustworthy results—not merely the number of automated tests.
8. Suggestions & Feedback
Help improve AutoQA Prep. Share a correction, suggest a question or topic, or tell us about your experience using this question bank.