Q26 of 37 · Selenium
How would you architect a Selenium framework from scratch for a Java team?
Short answer
Short answer: Layered: driver factory + config → page objects + components → test base classes → tests + data providers. Use Maven, TestNG, AssertJ, Allure, Selenium Manager, Docker Grid for CI. Optimise for readability of tests, isolation of locators, and parallel-safety from day one.
Detail
A clean Selenium framework has five layers, top to bottom:
1. Tests — the only layer the rest of QA reads regularly. Each test is one user journey, with assertions in the test (not in page objects), parameterised via @DataProvider where useful. Tests should read like the user's intent: new LoginPage(driver).loginAs(...).orderItem(...).expectConfirmation().
2. Page Objects + Component Objects — wrap pages and reusable fragments (navbar, modal, table row). Locators are private. Methods return either void, the same page (for chained actions), or the next page object. Never assert in page objects.
3. Test Base Classes — @BeforeMethod creates the driver, navigates to base URL, optionally logs in. @AfterMethod captures screenshot on failure and quits the driver. Listeners (retry, reporting) are wired here.
4. Driver Factory + Config — one source of truth for browser, headless, base URL, parallel mode. Reads from -D system properties, env vars, or a profile file. Must be thread-safe (ThreadLocal<WebDriver>) for parallel runs.
5. Utilities — explicit-wait helpers, JSON Schema validation, file fixtures, API helpers for state setup.
Stack picks I'd defend:
- Maven + Java 17 + Selenium 4.x — current LTS-aligned baseline.
- TestNG over JUnit 5 for parallel-test execution, data providers, and groups.
- AssertJ over TestNG asserts — much better failure messages.
- Allure for reports — historical trends, attachments, narratives.
- Selenium Manager (built into 4.10+) — no webdriver-manager dep.
- Docker compose Grid for CI — Chrome + Firefox nodes, hub on the build runner.
- Lombok sparingly —
@Slf4j,@Builderfor test data builders.
Folder layout:
src/main/java/.../
pages/ # page objects + components
factory/ # DriverFactory, BrowserConfig
listeners/ # RetryListener, ScreenshotListener
utils/ # waits, schemas, builders
src/test/java/.../
base/ # BaseTest with @BeforeMethod / @AfterMethod
tests/ # actual test classes
data/ # data providers
src/test/resources/
testng.xml # suite definition
fixtures/ # JSON / CSV test data
Things I'd push back on:
- Custom logging frameworks. Use SLF4J + Logback.
- Generated UI test cases from BDD that aren't actually used by non-devs.
- Page objects with hundreds of methods. Split into components when a class crosses ~100 lines.
The interview signal: layered separation, naming the parallel-safety boundary (ThreadLocal<WebDriver>), and being opinionated about tooling with reasons.
// EXAMPLE
DriverFactory.java
public class DriverFactory {
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static WebDriver get() {
if (driver.get() == null) {
driver.set(create());
}
return driver.get();
}
public static void quit() {
if (driver.get() != null) {
driver.get().quit();
driver.remove();
}
}
private static WebDriver create() {
String browser = System.getProperty("browser", "chrome");
boolean headless = Boolean.parseBoolean(System.getProperty("headless", "true"));
return switch (browser) {
case "chrome" -> {
ChromeOptions o = new ChromeOptions();
if (headless) o.addArguments("--headless=new");
yield new ChromeDriver(o);
}
case "firefox" -> new FirefoxDriver();
default -> throw new IllegalArgumentException("Unknown browser: " + browser);
};
}
}// WHAT INTERVIEWERS LOOK FOR
// COMMON PITFALL
// Related questions
Explain the Page Object Model with a concrete example.
Selenium
What is the architecture of Cypress and how does it differ from WebDriver-based tools?
Cypress
How would you architect a large Cypress test suite for a multi-team monorepo?
Cypress
What is the difference between abstract class and interface in Java?
Core Java