Lesson 13 · Pytest deep dive
pytest Tutorial #13: Temporary Files & Directories
On testing real Python code with pytest. tmppath and tmppathfactory: real disk-backed scratch space, auto-cleaned. Let's get into it. tmppath is a built-in…
- CoursePytest deep dive
- Lesson13 of 18
- FormatJupyter notebook · 6 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbpytest Deep-Dive, Video 13: Temporary Files#
- Video thirteen of an eighteen-part series on testing real Python code with pytest.
- tmp_path and tmp_path_factory: real disk-backed scratch space, auto-cleaned.
- Let's get into it.
Part 1: The Helper From Last Time#
import pytest, tempfile, os
def run_pytest(code_str, args=None):
with tempfile.NamedTemporaryFile(mode='w', suffix='_test.py', delete=False) as f:
f.write(code_str)
path = f.name
result = pytest.main([path, *(args or [])])
os.unlink(path)
return result
Part 2: tmp_path - a Fresh pathlib.Path Per Test#
code = '''
def test_tmp_path_is_a_directory(tmp_path):
assert tmp_path.is_dir()
assert tmp_path.exists()
def test_tmp_path_starts_empty(tmp_path):
assert list(tmp_path.iterdir()) == []
'''
exit_code = run_pytest(code, ['-v', '--no-header'])
print(exit_code)
Part 3: Writing and Reading Real Files#
code = '''
def test_write_and_read_file(tmp_path):
config_file = tmp_path / 'config.txt'
config_file.write_text('debug=true')
assert config_file.exists()
assert config_file.read_text() == 'debug=true'
def test_write_creates_real_file_on_disk(tmp_path):
data_file = tmp_path / 'data.csv'
data_file.write_text('a,b,c\\n1,2,3\\n')
assert data_file.stat().st_size > 0
'''
exit_code = run_pytest(code, ['-v', '--no-header'])
print(exit_code)
Part 4: Every Test Gets Its Own, Genuinely Unique tmp_path#
code = '''
seen_paths = []
def test_first_records_path(tmp_path):
seen_paths.append(str(tmp_path))
(tmp_path / 'marker.txt').write_text('first')
def test_second_records_path(tmp_path):
seen_paths.append(str(tmp_path))
marker = tmp_path / 'marker.txt'
assert not marker.exists()
def test_paths_were_different():
assert seen_paths[0] != seen_paths[1]
'''
exit_code = run_pytest(code, ['-v', '--no-header'])
print(exit_code)
Part 5: tmp_path_factory - Session-Scoped Temp Directories#
code = '''
import pytest
@pytest.fixture(scope='session')
def shared_data_dir(tmp_path_factory):
d = tmp_path_factory.mktemp('shared_data')
(d / 'lookup.txt').write_text('42')
return d
def test_reads_shared_data_first(shared_data_dir):
assert (shared_data_dir / 'lookup.txt').read_text() == '42'
def test_reads_shared_data_second(shared_data_dir):
assert (shared_data_dir / 'lookup.txt').exists()
'''
exit_code = run_pytest(code, ['-v', '--no-header'])
print(exit_code)
Part 6: A Real Pattern - Testing Config File Round-Trips#
code = '''
import json
def save_config(path, config):
path.write_text(json.dumps(config))
def load_config(path):
if not path.exists():
return {}
return json.loads(path.read_text())
def test_save_then_load_round_trips(tmp_path):
config_path = tmp_path / 'settings.json'
original = {'debug': True, 'workers': 4}
save_config(config_path, original)
loaded = load_config(config_path)
assert loaded == original
def test_load_missing_file_returns_empty(tmp_path):
missing_path = tmp_path / 'does_not_exist.json'
assert load_config(missing_path) == {}
'''
exit_code = run_pytest(code, ['-v', '--no-header'])
print(exit_code)
Wrap-Up: What You Learned#
- tmp_path is a built-in fixture handing each test its own fresh, empty pathlib.Path directory.
- The / operator builds paths inside it; write_text and read_text perform real disk I/O.
- tmp_path is function-scoped by default, so different tests never share or collide over the same directory.
- tmp_path_factory is session-scoped, supporting several distinct temp directories that persist across tests.
- tmp_path_factory.mktemp(name) creates one named, persistent subdirectory for shared session-level setup.
- Testing file I/O against tmp_path keeps a suite fast, isolated, and safe from touching real user files.
- That wraps up temporary files. Next up: Capturing Output.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



