Q29 of 38 · Test design

How do you design tests that remain maintainable as the system evolves?

Test designMidtest-designmaintainabilitypage-objecttest-architecturebest-practices

Short answer

Short answer: Design tests against behaviour and outcomes, not implementation details. Use the Page Object (or equivalent) pattern to isolate selectors, keep test data in fixtures, and name tests so that a failure message explains the broken behaviour without reading the code.

Detail

The main cause of brittle tests is coupling to implementation: asserting on DOM structure, internal state, or specific element IDs rather than observable behaviour. When the UI is redesigned or an API endpoint is refactored, every coupled assertion breaks simultaneously.

Practical design rules: Test through the public interface. API tests call endpoints, not internal functions. UI tests interact through visible controls, not implementation details like data-testid tied to component internals. Centralise selectors. In UI automation, all selectors live in Page Objects or component locator files. When a selector changes, one file changes — not 30 tests. Make test intent readable. A failing test message should describe the broken behaviour: "user cannot log in with valid credentials" not "element .btn-primary not found at line 42." Avoid tight data coupling. Tests that depend on a specific database row with a specific ID break whenever the seed changes. Use factories or test fixtures that create their own known state.

// WHAT INTERVIEWERS LOOK FOR

Testing against behaviour, not implementation. Page Object or equivalent for selector isolation. Readable failure messages. Avoiding tight coupling to specific seed data.