Test Framework Architecture
The 'design from scratch' narrative + core patterns + fixtures + CI/parallel. STOP before test platforms, custom runners. Over-patterning is a negative signal.
- The 3-minute "design a framework from scratch" answer
A crisp, ordered walkthrough (goal/scope -> layers -> POM -> config/env -> fixtures -> data -> patterns -> reporting -> CI -> scale -> trade-offs) that frames you as the framework OWNER, not a script-adder.
'How would you design a test automation framework from scratch?' / 'Walk me through YOUR framework.' (near-guaranteed anchor question)
- pytest.ini / pyproject.toml config
The central pytest config: registered markers, addopts, testpaths, log_cli, and plugin settings.
'Where do you register markers / set default options?' / live-coding a config file
- Layered project structure + separation of concerns
Standard folders (tests/ pages/ api/ utils/ config/ testdata/ reports/ + conftest.py + pytest.ini/pyproject.toml) each with a single responsibility so a UI change touches one layer.
'How is your framework structured / organised?' / 'Where does test logic vs page logic vs infra live?'
- OOP fundamentals applied to test code
Encapsulation, inheritance, composition, polymorphism as used in a framework (BasePage inheritance, encapsulated locators).
'Explain OOP concepts with a testing example' (frequent warm-up)
- Page Object Model (locators as attributes, actions as methods)
One class per page/component; locators are class attributes, business actions are methods (login(user,pw) not raw fills) so tests read like business steps and a UI change is fixed in one place.
'How do you structure UI tests?' / 'What is POM and why use it?' / live-code a page object
- BasePage + fluent/chained POM + component objects
A shared BasePage (common waits/actions), methods that return the next page object (fluent chaining), and component objects (header, modal) vs full-page objects.
'How do you avoid duplication across pages?' / 'What's a fluent page object?'
- Singleton pattern
One shared instance with a global access point; classic framework use = single config/logger (and, in Selenium, one WebDriver).
'What is the Singleton pattern and why do you need it in a framework?' (most-asked pattern question)
- Factory pattern
Create an object (browser/driver, API client) from config behind one interface without the caller naming the concrete class.
'What is the Factory pattern - give a framework use case?' / 'How do you add cross-browser support without rewriting tests?'
- Page Object pattern (as a design pattern)
POM is itself the canonical test design pattern; encapsulate UI so change is localized.
'Which design patterns have you used in your framework?'
- Strategy pattern
Swap behavior at runtime selected from config - e.g. auth strategy (token vs cookie vs SSO) or data-source strategy.
'How would you support multiple auth mechanisms / environments cleanly?'
- Builder pattern for test data
Fluent construction of complex objects (UserBuilder().with_role('admin').with_kyc(False).build()) instead of giant dict literals.
'How do you construct complex test data?' / 'name a pattern beyond POM'
- Other GoF patterns (Command/Observer/Facade/Decorator)
Know they exist and roughly where they'd fit; forcing every GoF pattern into tests is an anti-signal.
'What other patterns do you know?' (only as a probe)
- conftest.py + fixture scopes
conftest.py shares fixtures with no import; scopes (function/class/module/package/session) control lifetime; browser/page lifecycle lives here.
'Difference between conftest.py and a normal fixture file?' / 'Explain fixture scopes' / live-code a fixture with teardown
- autouse, yield teardown, addfinalizer, conftest hierarchy
autouse fixtures run without request; yield/addfinalizer handle finalization; nested conftest.py files override/extend parent ones.
'How does teardown work?' / 'What is autouse?' / 'How do fixtures compose across dirs?'
- Playwright-provided fixtures (page/context/browser, worker vs test scope)
pytest-playwright ships page/context/browser fixtures; each test gets an isolated browser context (clean state), distinct from a shared Selenium driver.
'How does Playwright isolate tests?' / 'How do you reuse login state?'
- Custom pytest hooks (pytest_addoption, pytest_generate_tests, pytest_runtest_makereport)
Extend pytest: add CLI flags (--env/--browser), dynamically parametrize from a flag, and screenshot/trace-on-fail via the makereport hook.
'Have you written a custom pytest plugin/hook?' / live-code a --browser option via pytest_addoption
- Externalized config + --env selection across dev/qa/stage/prod
No hardcoded URLs/creds; config in JSON/YAML/.env; one suite runs any environment by flipping a flag.
'How do you manage running in different environments?' / 'Where do base URLs/timeouts live?'
- Secrets / credential management
Credentials via env vars / CI secrets / a vault - never committed to the repo.
'How do you manage credentials/secrets inside your framework?' (named question)
- Data-driven testing (parametrize + external data)
@pytest.mark.parametrize and external JSON/CSV: one test, many data rows; know its limits (data explosion, readability).
'How do you handle test data / run one test over many inputs?' / live-code parametrization from JSON
- Keyword-driven & hybrid frameworks
Keyword-driven = actions abstracted as keywords (Robot Framework); hybrid = data+POM(+BDD) combined = what most real frameworks are.
'Data-driven vs keyword-driven vs hybrid - differences and when to use each?'
- BDD (pytest-bdd / Cucumber-Gherkin)
Given/When/Then business-readable specs mapped to step defs; pros (stakeholder readability) vs cons (ceremony/overhead).
'Have you used BDD/Cucumber?' / 'When is BDD worth it?'
- External data loading + factories/faker + isolation-safe data
Load JSON/CSV via a util; generate volume with faker/factories; make data unique per worker so parallel runs don't collide; DB seed/cleanup.
'How do you handle test data?' / 'How do you keep data safe under parallel runs?'
- Reusable utility layer (waits, API client wrapper, logging, soft-assert)
Custom explicit waits (not sleep), a thin requests/API-client wrapper (base URL + auth injection + response validation), logging config, custom/soft assertions.
'What reusable utilities does your framework provide?' / 'How do you handle waits?'
- Allure / pytest-html + JUnit XML + artifacts on failure
Rich reports (Allure steps/attachments/trends or pytest-html), JUnit XML for CI, screenshot + Playwright trace attached on failure.
'What kind of reports do you generate?' / 'How do you capture evidence on failure?'
- Flake control
Explicit waits over sleeps, test isolation, deterministic data, idempotent setup/teardown; pytest-rerunfailures sparingly WITH root-cause culture.
'How do you handle flaky tests?' (very common)
- Execution flow of the framework
collection -> fixture/setup -> test -> teardown -> report/exit-code; the lifecycle pytest drives.
'Tell me about the execution flow of your framework.'
- Marks & suite selection
@pytest.mark.smoke/regression/sanity, -m selection, markers registered in config; CI triggers the right subset.
'How do you select which tests run in which pipeline?'
- CI integration (GitHub Actions / Jenkins) + smoke vs regression split
Tests run on push/PR: checkout -> deps -> run -> upload artifacts -> exit code gates the pipeline; smoke on every push, regression nightly, selected via marks.
'What's your CI strategy?' / 'How does your suite run automatically?' / 'Smoke vs regression triggers?'
- Parallel execution + test isolation (pytest-xdist)
pytest -n auto (xdist), --dist loadscope/loadgroup; requires independent tests, no shared mutable state, isolated Playwright contexts, unique data per worker.
'How do you run tests in parallel? What breaks when you do?' / 'How did you cut regression time?'
- Self-healing locators / AI-assisted generation / agentic testing
AI/LLM-driven test generation, self-healing locators (Healenium/Mabl), and agent-based test creation as maintainability/coverage multipliers - a 2026 framework theme and this candidate's genuine edge.
'How could AI improve/maintain your framework?' / 'Have you used AI-assisted testing tools?' (rising in 2026)
- Installable internal package + distributed custom plugin
Framework as a versioned pip-installable package reused across teams, and a real pytest plugin distributed via entry points (not just conftest hooks).
'How would you make the framework reusable across multiple teams?'
- Docker / grid / cloud cross-browser + sharding across runners
Dockerized runs, Selenium Grid / Playwright grid / BrowserStack-LambdaTest, and matrix/sharded parallelism across CI runners.
'How do you scale cross-browser at high volume?'
- Contract testing (Pact) + service virtualization / mock servers
Consumer-driven contract tests and mock servers to isolate services in API testing.
'How do you test a service whose dependencies aren't ready?'
- Company-wide test PLATFORM / custom runner / deep pytest internals / K8s elastic grid
Multi-language plugin-ecosystem platform, hand-rolled runner/assertion engine, complex pytest_collection_modifyitems/hookwrappers, self-built K8s autoscaling grid.
Only when the interviewer pushes past mid-level scope (a probe for judgment/humility)