Resource library

Automation Interview

Pytest Interview Questions and Answers

Pytest interview questions and answers on fixtures, scope, conftest, parametrize, markers, monkeypatch mocking, plugins, and parallel runs with pytest-xdist.

2,377 words | Article schema | FAQ schema | Breadcrumb schema

Overview

Pytest is the framework most Python teams reach for, and interviewers know it well enough to tell a real user from someone who has only run pytest with no arguments. The depth is in the fixture system: scope, dependency injection, finalization, conftest sharing, and the plugin ecosystem built on top. If you can talk fluently about fixtures and parametrization, you clear most pytest rounds. If you cannot, no amount of Selenium knowledge will cover for it.

This guide is deliberately pytest-specific. It walks through the fixture questions that dominate these interviews, then parametrization, markers and test selection, mocking with monkeypatch and pytest-mock, the plugins that matter (pytest-xdist, pytest-cov, pytest-html, pytest-bdd), configuration, and the debugging scenarios interviewers use to see whether you have actually maintained a suite. Every question includes the answer a strong candidate gives, with the specific syntax and behavior named.

If you are still shaky on core Python, study that first, because pytest questions assume you already understand generators, decorators, and exceptions. Here we assume the fundamentals and go deep on the framework.

Why Pytest Interviews Live and Die on Fixtures

When an interviewer asks about pytest, they are almost always steering toward fixtures within a minute or two, because fixtures are what separate pytest from a bag of test functions. They provide dependency injection, controlled setup and teardown, and reuse across a suite, and they are where beginners get the behavior subtly wrong. Expect the conversation to move from 'what is a fixture' to 'what scope did you use and why' quickly, so have a real example ready.

The secondary theme is maintainability at scale. Pytest makes it easy to write a hundred tests; it takes skill to keep them fast, isolated, and readable. Questions about conftest, parametrization, and markers are really questions about whether you can organize a growing suite. Answer with the reasoning behind your choices, not just the syntax, and you will read as someone who has felt the pain of a suite done wrong.

Fixture Fundamentals

'What is a pytest fixture, and how do you use one?' A fixture is a function decorated with @pytest.fixture that provides a fixed baseline (data, a browser, a DB connection) to tests. A test requests it simply by naming it as a parameter, and pytest injects the return value: def test_login(browser) receives whatever the browser fixture yields. This dependency-injection model is cleaner than xUnit-style setUp and tearDown because each test declares exactly what it needs, and fixtures can depend on other fixtures.

'How do you do setup and teardown in a fixture?' Use yield. Everything before the yield runs as setup, the yielded value is handed to the test, and everything after the yield runs as teardown, guaranteed even if the test fails, much like a context manager. For example, a browser fixture creates the driver, yields it, then calls driver.quit() after the yield. The older addfinalizer method still exists, but yield is the idiomatic, readable choice today.

Fixture Scope and Finalization

'Explain fixture scope and the trade-off.' Scope controls how often a fixture is created: function (the default, fresh per test), class, module, package, and session (created once for the whole run). Broader scope is faster because you build the expensive thing once, but it risks state leaking between tests that share the instance. The rule of thumb: use the narrowest scope that keeps tests isolated, and reserve session scope for genuinely read-only or expensive resources like a database container or an authenticated API token.

'You made a fixture session-scoped and now tests interfere with each other. Why?' Because they share one mutable instance. If tests mutate a session-scoped object (say a shared user record or a logged-in browser session), later tests inherit that dirty state and fail depending on order. The fix is either to narrow the scope so each test gets a clean instance, or to keep the shared resource read-only and put the mutable, per-test data in a function-scoped fixture layered on top. This question tests real experience, not syntax.

conftest.py and Fixture Sharing

'What is conftest.py, and why does it matter?' conftest.py is a special file pytest discovers automatically, where you put fixtures and hooks you want shared across multiple test files without importing them. Fixtures defined there are available to every test in that directory and its subdirectories, so a driver or api_client fixture lives in the top-level conftest and just works everywhere. It is also where plugin hooks and custom command-line options go. The key insight: pytest resolves fixtures by name through the conftest hierarchy, nearest first.

'Can you override a fixture, and how?' Yes. A fixture defined in a more specific conftest, or in the test module itself, overrides one of the same name from a higher-level conftest. This lets you set a sensible default globally and specialize it for a subset of tests, for example a general config fixture overridden with staging values in one folder. Understanding this override hierarchy is what lets you keep fixtures DRY without copy-pasting setup into every file.

Parametrization Questions

'How does parametrization work, and why use it?' @pytest.mark.parametrize runs the same test function across multiple inputs, each as a separate reported test case, so one function covers many scenarios without a loop. You pass argument names and a list of value tuples, and pytest expands them: five login combinations become five named tests that pass or fail independently. This beats looping inside one test because a loop stops at the first failure and reports as a single test, hiding which case broke.

'How do you parametrize a fixture instead of a test?' Add params to the fixture decorator: @pytest.fixture(params=[...]). Every test that uses the fixture then runs once per param value, which is how you run an entire suite across, say, three browsers or two database backends. Access the current value through request.param inside the fixture. Mention pytest.param with ids and marks when you want readable test names or need to xfail a single known-bad combination.

Markers and Test Selection

'What are markers, and how do you use them?' Markers tag tests with metadata using @pytest.mark.name, and you select them at runtime with -m. Built-ins include @pytest.mark.skip and skipif for conditional skipping, xfail for known failures you still want tracked, and parametrize. Custom markers like smoke or slow let you slice the suite, so pytest -m smoke runs only the smoke tests. Register custom markers in pytest.ini or pyproject.toml so a typo does not silently create a new, empty marker.

'How do you run a subset of tests?' Several ways worth naming: -m for markers, -k for a keyword expression matching test names (pytest -k login and not slow), passing a file or a specific node id path, and --lf or --ff to rerun last-failed or failed-first. In CI you combine these: a fast smoke marker on every commit, the full suite nightly. Showing you know the selection flags signals you have run large suites where running everything every time was not an option.

Mocking: monkeypatch and pytest-mock

'What is monkeypatch, and when do you use it?' monkeypatch is a built-in fixture that safely modifies attributes, dictionary entries, or environment variables for the duration of one test, then restores them automatically. Reach for it to replace a function, set an env var, or stub an attribute without a separate library, for example monkeypatch.setenv to point config at a test value, or monkeypatch.setattr to swap a slow network call for a stub. Its big advantage is automatic cleanup, so you never leak a patched global into other tests.

'monkeypatch versus pytest-mock, which do you use?' monkeypatch is enough for simple attribute and env swaps. pytest-mock wraps the standard unittest.mock as a mocker fixture, so you use it when you need real mock objects with assertions on how they were called (mocker.patch then assert_called_once_with) or return-value sequences. The rule: monkeypatch to replace, mocker when you also need to verify the interaction. Both auto-clean, unlike a bare unittest.mock.patch used incorrectly.

Plugins and Parallel Execution

'Which pytest plugins have you used?' Name them with a purpose: pytest-xdist to run tests in parallel across CPU cores or machines, pytest-cov for coverage reports, pytest-html for a shareable report, pytest-mock for mocking, pytest-rerunfailures to retry flaky tests, and pytest-bdd for Gherkin-style specs. Tying each plugin to a problem it solves shows you have felt the need, rather than listing what you skimmed in the docs.

'How do you run pytest tests in parallel, and what breaks?' pytest-xdist with -n auto distributes tests across worker processes. The catch is that parallelism exposes hidden coupling: tests that share files, database rows, or a session-scoped mutable fixture start colliding. So the real answer covers prerequisites: test independence, isolated data per worker (xdist exposes a worker id you can use to namespace data), and no reliance on execution order. Parallelism is a test-design problem before it is a flag.

Configuration and Assertions

'How do you configure pytest?' Central config lives in pytest.ini, pyproject.toml (under a tool.pytest.ini_options table), setup.cfg, or tox.ini. There you set default options (addopts), test discovery patterns, registered markers, and log settings. Committing this config is what makes pytest behave identically on every developer machine and in CI, so nobody depends on remembering the right command-line flags.

'Why does pytest not need self.assertEqual like unittest?' Pytest uses assertion rewriting: it rewrites plain assert statements at import time so a failing assert x == y prints a rich, introspected diff of both sides, not just a bare failure message. That is why pytest tests read as plain Python with bare asserts, which is more readable than the assertEqual family. For checking that code raises, use the pytest.raises context manager, and pytest.approx for floating-point comparisons.

Debugging and Scenario Questions

'A test passes alone but fails in the full suite. How do you debug it?' This almost always means shared state or ordering dependence. Reproduce it by running the failing test right after a suspected culprit, use a random-order plugin to check order sensitivity, and add --setup-show to see which fixtures run and when. The root cause is usually a too-broad fixture scope or a global mutated somewhere; the fix is proper isolation, not a retry. Narrate this and you sound like someone who has actually debugged a suite.

'How do you handle a genuinely flaky test?' First try to make it deterministic: fix the wait, the shared data, or the race. If it is a known external flakiness you cannot remove immediately, quarantine it (a marker plus pytest-rerunfailures) and file a ticket, rather than letting it fail the pipeline at random. Be explicit that blanket auto-retries hide real bugs, so retries are a stopgap with a tracking ticket, not a solution.

Behavioral and Design Questions

'How do you keep a large pytest suite maintainable?' Lead with fixtures at the right scope in a clean conftest hierarchy, parametrization instead of copy-pasted tests, markers to slice fast versus slow, and naming that reads as behavior. Add coverage and a report in CI, and enforce test independence so the suite can parallelize. The theme interviewers want: you actively fight entropy, because a test suite decays into a slow, flaky liability if nobody curates it.

'Pytest or unittest for a new project, and why?' Pytest almost every time: less boilerplate (plain asserts, no class required), the powerful fixture system, parametrization, and a rich plugin ecosystem, while still being able to run existing unittest test cases. unittest's only real edge is being in the standard library with no install. Frame it as a productivity and maintainability choice, and note that pytest can run unittest tests, which makes migration low-risk and reassures a team sitting on legacy tests.

Frequently Asked Questions

What is the most important pytest topic to prepare?

Fixtures, without question. Be ready to explain what a fixture is, yield-based setup and teardown, scope and its trade-offs, and conftest sharing. Most pytest interviews steer to fixtures within the first two minutes, and depth there carries the round.

What is the difference between a fixture and a marker?

A fixture supplies setup, data, or a resource that a test requests by name, running code around the test. A marker is metadata that tags a test (skip, xfail, slow, smoke) for selection and behavior. Fixtures provide things; markers label tests.

When should I use function scope versus session scope?

Default to function scope so every test gets a fresh, isolated instance. Move to session scope only for expensive, effectively read-only resources like a database container or an auth token. If tests mutate a session-scoped object, they will interfere with each other depending on run order.

How do I run pytest tests in parallel?

Install pytest-xdist and run with -n auto to spread tests across CPU cores. The prerequisite is test independence: no shared files, rows, or mutable session fixtures, and no reliance on execution order. Use the xdist worker id to namespace per-worker test data.

What is the difference between monkeypatch and pytest-mock?

monkeypatch is built in and best for simple replacements of attributes, dict entries, and environment variables, with automatic restoration. pytest-mock provides the mocker fixture wrapping unittest.mock, which you use when you also need to assert how a mock was called. Both clean up automatically.

Is pytest better than unittest?

For most new projects, yes: plain assert statements with rich failure output, a powerful fixture system, parametrization, and a large plugin ecosystem, with far less boilerplate. pytest can also run existing unittest tests, so adopting it does not require rewriting a legacy suite.

How do you register a custom marker in pytest?

Declare it under markers in pytest.ini or the tool.pytest.ini_options table in pyproject.toml. Registering it prevents typos from silently creating new empty markers, stops the unknown-marker warning, and documents the marker for the rest of the team.

Related QAJobFit Guides