Built-in fixtures: tmp_path, capsys, monkeypatch

pytest · Practical · 12 min

These built-ins cover most everyday test needs and are safer than hand-rolled temp dirs or global patches.

pytest ships fixtures you can request by name anywhere:

  • `tmp_path`: a unique, empty pathlib.Path directory per test. Write files there instead of the working directory. tmp_path_factory is the session-scoped variant. Old directories are cleaned up automatically, keeping the last few runs for inspection.
  • `capsys`: captures stdout and stderr; capsys.readouterr() returns an object with .out and .err and resets the buffer. capfd does the same at file descriptor level, catching output from subprocesses and C extensions.
  • `monkeypatch`: temporarily changes things and undoes them after the test: setattr(obj, "name", value), setenv("API_KEY", "x"), delenv, setitem(dict, key, value), chdir(path), syspath_prepend.
  • `caplog`: captures log records (covered later).
  • `request`: information about the requesting test, used inside fixtures.

Because every change made through monkeypatch is reverted at teardown, tests can't leak modified environment variables or patched functions into each other, which is the main danger of patching by hand.

pytest --fixtures lists every available fixture, including those from plugins and your conftest files, with their docstrings.

Example

# test_builtins.py
import os

def save_report(path, lines):
    path.write_text("\n".join(lines))
    print(f"saved {len(lines)} lines")

def api_url():
    return os.environ.get("API_URL", "https://api.example.com")

def test_save_report(tmp_path, capsys):
    target = tmp_path / "report.txt"
    save_report(target, ["a", "b"])
    assert target.read_text() == "a\nb"
    assert capsys.readouterr().out == "saved 2 lines\n"

def test_api_url_override(monkeypatch):
    monkeypatch.setenv("API_URL", "http://localhost:8000")
    assert api_url() == "http://localhost:8000"

def test_api_url_default(monkeypatch):
    monkeypatch.delenv("API_URL", raising=False)
    assert api_url() == "https://api.example.com"

Remember

  • tmp_path: fresh temp directory per test
  • capsys.readouterr() returns captured .out and .err
  • monkeypatch changes env, attributes and dicts and undoes them
  • pytest --fixtures lists everything available

Watch out for

  • Setting os.environ directly in a test and breaking later tests that expect the default.

Check yourself

  1. Why prefer monkeypatch.setenv over assigning os.environ['X'] in a test?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.pytest.org/en/stable/reference/fixtures.html