Designing a Framework from Scratch: The Layers
Up to now you've written tests. Now you'll design the thing tests sit on top of. It sounds like a big leap, but it comes down to one habit: keep each kind of change in its own box.
The idea, in one line
Split the framework into layers, so a change in one place never ripples through everything else. That's what "architecture" really means here.
The six layers
- Config — environment settings: base URLs, timeouts, credentials. Pull these from env vars, never hardcode them.
- Core — the plumbing: the driver or client factory, a base test class, shared waits and helpers.
- Pages (or API clients) — wrap the app so tests speak in plain terms like
login_page.sign_in(user), not raw selectors. - Data — factories and fixtures that build valid inputs: users, orders, payloads.
- Tests — thin. Arrange, act, assert. They should read like a spec.
- Reporting — captures results, screenshots, and logs as saved artifacts.
Why split it this way
The rule behind the split is how often each thing changes. Selectors change all the time, so they live in Pages. Environments change every run, so they live in Config. Tests should barely change at all. When you isolate the fast-moving parts, one edit stays one edit.
# A layered project keeps change contained
# framework/
# +- config/ # settings, env vars, base URLs
# | settings.py
# +- core/ # driver factory, base classes, waits
# | base_page.py
# +- pages/ # one class per screen (UI) or resource (API)
# | login_page.py
# +- data/ # factories + builders for test inputs
# | user_factory.py
# +- tests/ # thin: arrange, act, assert
# | test_login.py
# +- conftest.py # fixtures wire the layers together
#
# A selector change touches only pages/
# A new environment touches only config/ -- never tests/Read it top to bottom: each folder owns one job. If a change forces you to edit several folders at once, that's a sign the layers are leaking into each other.
Advanced — what interviewers listen for
When someone asks "how is your framework structured," the layered list is only half the answer. The other half is the why — tie each layer back to its rate of change. That reasoning is what marks you out as an architect rather than a test writer.
Grounded in the pytest docs (good integration practices) and the Page Object Model guidance in the Selenium docs