Scenario and Coding Questions
On this page
Senior interviews rarely stop at "what does X do". They hand you a situation and listen to how you reason. There is no single correct answer to these questions; the interviewer is grading your structure, the trade-offs you name, and whether you ask clarifying questions before you start designing.
"Design a test automation framework for our web shop"#
Start with questions, not with a tool: Which languages does the team write? How often do they release? What breaks most often in production? Is there an API you can use for setup? Which browsers and devices do customers use?
Then lay out the layers and justify each:
| Layer | Decision | Why |
|---|---|---|
| Test pyramid | Most checks as unit and API tests, a thin layer of UI journeys (search, cart, checkout, account) | UI tests are the slowest and most fragile; use them for what only they can prove |
| Runner | Playwright Test, or Selenium with TestNG/JUnit 5 if the team writes Java | Match the team's language so developers contribute |
| Structure | Page objects or components, fixtures for setup | Locators change in one place; see the Page Object Model course |
| Test data | Created per test through the API, unique names, cleaned up after | Enables parallel runs and removes order dependencies |
| Environments | Base URL and credentials from environment variables | Same suite runs locally, on staging and in CI |
| Browsers | Chromium on every pull request; full browser and device matrix nightly | Fast feedback first, coverage second |
| Reporting | HTML report with trace, video or screenshot on failure | A failure must be diagnosable without re-running |
| Ownership | Flaky tests get an owner and a deadline | Trust in the suite is the real deliverable |
- Red flag: Opening with "I'd use Cucumber with an Excel data layer" before knowing who will read the scenarios.
"This test is flaky. Walk me through it."#
Treat it like a production incident:
-
Measure. How often does it fail, since when, in which environment? Re-run it in isolation 50 times (
--repeat-each=50in Playwright,invocationCountin TestNG), then together with the rest of the suite in parallel. - Read the evidence from a failed run: trace or video, console errors, network log, server logs. Look at what the page actually showed at the moment of failure.
- Classify. Timing (an assertion that does not wait, an animation), shared data (two tests edit the same record), order dependency (a test relies on state left by another), environment (CI is slower, a third-party script is slow, a different viewport), or a real product race condition.
- Fix the root cause. Replace sleeps with condition-based waits, make data unique, remove the dependency. If it is a product bug, file it with the evidence.
-
Prevent recurrence. Add a lint rule against
waitForTimeoutorThread.sleep, track retried tests in the report.
Interviewers often follow with "and if you can't fix it this sprint?" The answer is a quarantine tag that excludes it from the blocking run, with a ticket, rather than deleting it or adding retries silently. Our handling test flakiness article has more patterns for step 3.
"How would you run 500 UI tests in 10 minutes?"#
Do the arithmetic out loud. If the average test takes 60 seconds, the suite is 500 minutes of work. Ten minutes of wall-clock time therefore needs at least 50 tests running at once, and more in practice because of startup overhead and the slowest test setting the floor.
Then name what has to be true:
-
Isolation: every test independent, with its own data, so any test can run on any worker in any order.
-
Capacity: 50 or more concurrent browsers, from your own Grid or Kubernetes cluster, or from a cloud grid. A parallel testing calculator helps size this against your suite.
-
Distribution: Playwright workers plus
--shardacross CI machines, TestNGparallelwith enough threads, or Cypress spec-level parallelisation. Balance by duration, not count; one 8-minute test alone ruins the target. -
Less work per test: log in via stored state or API, seed data through the API, test one thing per test.
-
Fewer UI tests: ask whether all 500 need a browser. Pushing checks down to the API layer is often the biggest win.
-
Red flag: "Buy more machines" with no mention of test isolation. Parallelising order-dependent tests just makes them fail faster.
Your suite has 300 UI tests averaging 90 seconds each, and the target is 15 minutes of wall-clock time. What is the minimum number of concurrent sessions, before startup overhead?
300 tests at 1.5 minutes is 450 minutes of work; 450 / 15 = 30 sessions at once, and more in practice because of startup time and the slowest test setting the floor.
Live coding: find broken links on a page#
A classic exercise. Show that you can mix WebDriver with a plain HTTP client and handle edge cases.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class BrokenLinks {
private static final HttpClient HTTP = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(10))
.build();
public static List<String> find(WebDriver driver) {
return driver.findElements(By.cssSelector("a[href]")).stream()
.map(a -> a.getDomProperty("href")) // absolute URL
.filter(href -> href != null && href.startsWith("http"))
.distinct()
.filter(href -> status(href) >= 400)
.collect(Collectors.toList());
}
private static int status(String url) {
try {
HttpRequest head = HttpRequest.newBuilder(URI.create(url))
.method("HEAD", HttpRequest.BodyPublishers.noBody())
.timeout(Duration.ofSeconds(10))
.build();
return HTTP.send(head, HttpResponse.BodyHandlers.discarding()).statusCode();
} catch (Exception e) {
return 599; // treat network errors as broken
}
}
}
Points that earn credit: using the DOM property so relative links are resolved, skipping mailto: and javascript: links, de-duplicating, and mentioning that some servers reject HEAD (fall back to GET on a 405) and that checking in parallel would be faster.
Live coding: verify a list is sorted by price#
import { test, expect } from '@playwright/test';
test('sort by price ascending', async ({ page }) => {
await page.goto('/shop');
await page.getByLabel('Sort by').selectOption('price-asc');
const prices = page.getByTestId('product-price');
await expect(prices.first()).toBeVisible();
const values = (await prices.allTextContents())
.map(text => Number(text.replace(/[^0-9.]/g, '')));
expect(values).toEqual([...values].sort((a, b) => a - b));
});
The detail interviewers look for: allTextContents() does not wait, so the test first waits for the list to render. A stronger candidate also notes the list might still show the old order at that moment and waits for a signal of the re-sort, such as the URL changing or the sort request finishing.
Questions to ask them#
Good candidates finish by interviewing back: Who owns failing tests? How long does the pipeline take today? What percentage of runs are red for reasons unrelated to the change? The answers tell you what the job really is.