Framework Design interview question
What layers does a maintainable test automation framework have?
Short answer
Separate concerns: tests that read like scenarios, a domain or flow layer, interaction layers (page or component objects, API clients), core utilities (driver lifecycle, config, data builders, waits), and reporting. Dependencies point downward only, so a UI change touches one page object, not hundreds of tests.
Answer
A framework is maintainable when a change in the product requires a change in one obvious place. Layering is how you get there: each layer has one job and depends only on the layers below it.
The layers
1. Tests
Test classes or spec files describe a scenario and its assertions. They read like the behaviour being checked and contain no locators, raw HTTP calls, sleeps or environment URLs.
@Test
void memberSeesLoyaltyDiscountAtCheckout() {
Customer member = testData.loyaltyMember();
api.basket().add(member, Products.HEADPHONES);
CheckoutPage checkout = app.loginAs(member).openCheckout();
assertThat(checkout.discountLabel()).isEqualTo("Loyalty 10%");
}
2. Flows or domain steps (optional but valuable)
Reusable multi-step business actions such as loginAs, placeOrder or createTenantWithAdmin. These often mix API and UI calls, for example creating data through the API and only driving the UI for the part under test.
3. Interaction layer
- Page and component objects own locators and expose intent-level methods (
checkout.applyCoupon("SAVE10")). Components (header, date picker, data table) are objects in their own right and are reused across pages. - API clients wrap endpoints, serialization and authentication, returning typed models.
- Database or message helpers for verification, kept read-only where possible.
Page objects should not contain assertions about business outcomes; they return state and the test decides what is correct.
4. Core and infrastructure
- Driver or browser lifecycle (factory, thread-safe instances, cleanup).
- Configuration loading per environment.
- Test data builders and factories.
- Custom waits, retry helpers for genuinely eventual operations, and logging.
5. Reporting and diagnostics
Screenshots, videos, traces, HTTP logs and environment metadata attached to each failure, published in a report such as Allure or the runner’s HTML report. This layer decides how fast someone can triage a red build.
Principles that keep it healthy
- Dependencies point one way. Tests use flows, flows use page objects and clients, those use core. Core never imports a page object.
- One place to change. A locator exists once; an endpoint path exists once; a timeout value exists once.
- No logic in tests that belongs below. If three tests contain the same five lines, it is a missing flow method.
- Keep the framework thin. Wrapping every WebDriver call in a custom method hides useful APIs and makes upgrades harder. Add abstractions when duplication appears, not in anticipation.
- Framework code gets reviewed and tested like product code, including linting and type checks.
Example layout
src/test/java/com/acme/
tests/ # specs grouped by feature
flows/ # business-level steps
pages/ # page and component objects
api/ # API clients and models
data/ # builders, factories
core/ # driver factory, config, waits, listeners
src/test/resources/
config/ # environment property files
What interviewers look for
- Clear separation between intent (tests) and mechanics (locators, HTTP).
- Use of API calls for setup to keep UI tests short.
- Awareness of over-engineering risks, not just more layers.
- Diagnostics treated as a first-class part of the design.
A strong answer covers
- Test layer contains intent and assertions, no low-level locators or HTTP details
- Interaction layer: page/component objects and API clients encapsulate locators and endpoints
- Core/infrastructure layer: driver lifecycle, config, waits, data builders, logging
- Reporting and observability: screenshots, traces, logs attached to failures
- Dependency direction and principles: one-way dependencies, single place to change