Python · Testing with pytest · Beginner
Fixtures
Use @pytest.fixture for reusable setup and teardown without boilerplate.
Quick topic start and explanations before exercises (exercises below):
Fixture Patterns You Will Use Daily
#Fixture Quick Reference
#Exercises:
Write a Fixture Used by Two Tests
#Create a `@pytest.fixture` called `numbers` that returns `[3, 1, 4, 1, 5, 9, 2, 6]`. Write two test functions that receive it as a parameter: one asserts `sum(numbers) == 31`, the other asserts `max(numbers) == 9`.
import pytest
@pytest.fixture
def numbers():
pass # return the list
def test_sum(numbers):
pass
def test_max(numbers):
pass
Solution
import pytest
@pytest.fixture
def numbers():
return [3, 1, 4, 1, 5, 9, 2, 6]
def test_sum(numbers):
assert sum(numbers) == 31
def test_max(numbers):
assert max(numbers) == 9
Fixture with yield for Cleanup
#Write a `@pytest.fixture` called `temp_csv` that creates a temporary CSV file with two lines of data using `tmp_path`, yields the file path, then removes the file. Write tests that read the file and verify the line count and header.
import pytest
@pytest.fixture
def temp_csv(tmp_path):
path = tmp_path / 'data.csv'
# write two lines to path
yield path
# cleanup
def test_csv_has_two_lines(temp_csv):
pass
Solution
import pytest
@pytest.fixture
def temp_csv(tmp_path):
path = tmp_path / 'data.csv'
path.write_text('name,age\nAlice,30\n')
yield path
if path.exists():
path.unlink()
def test_csv_has_two_lines(temp_csv):
lines = temp_csv.read_text().strip().splitlines()
assert len(lines) == 2
def test_csv_header(temp_csv):
first_line = temp_csv.read_text().splitlines()[0]
assert first_line == 'name,age'
Config Fixture for a Function Under Test
#Write a `@pytest.fixture` called `app_config` returning `{'base_url': 'https://api.example.com', 'timeout': 10, 'retries': 3}`. Write `build_url(config, path)` that joins `base_url` and `path` cleanly (no double slashes). Test it using the fixture.
import pytest
@pytest.fixture
def app_config():
pass # return the config dict
def build_url(config, path):
pass # join base_url and path
def test_build_url(app_config):
pass
Solution
import pytest
@pytest.fixture
def app_config():
return {'base_url': 'https://api.example.com', 'timeout': 10, 'retries': 3}
def build_url(config, path):
return config['base_url'].rstrip('/') + '/' + path.lstrip('/')
def test_build_url(app_config):
url = build_url(app_config, '/users')
assert url == 'https://api.example.com/users'
def test_build_url_no_double_slash(app_config):
url = build_url(app_config, 'products')
assert '//' not in url.split('https://')[-1]
def test_config_timeout(app_config):
assert app_config['timeout'] == 10
Use Two Fixtures in One Test
#Create two fixtures: `base_url` returning `'https://api.example.com'` and `headers` returning `{'Content-Type': 'application/json', 'Accept': 'application/json'}`. Write a test that uses both and asserts the URL is HTTPS and the headers contain the right keys.
import pytest
@pytest.fixture
def base_url():
pass
@pytest.fixture
def headers():
pass
def test_request_setup(base_url, headers):
pass
Solution
import pytest
@pytest.fixture
def base_url():
return 'https://api.example.com'
@pytest.fixture
def headers():
return {'Content-Type': 'application/json', 'Accept': 'application/json'}
def test_request_setup(base_url, headers):
assert base_url.startswith('https://')
assert 'Content-Type' in headers
assert headers['Content-Type'] == 'application/json'
assert 'Accept' in headers
Fixture Chain
#Create three fixtures in a chain: `db_config` returns a dict with `host` and `port`, `db_connection` receives `db_config` and returns `{'status': 'connected', ...}`, `db_cursor` receives `db_connection` and returns `{'query': None, 'connection': ...}`. Write a test that only requests `db_cursor`.
import pytest
@pytest.fixture
def db_config():
pass
@pytest.fixture
def db_connection(db_config):
pass
@pytest.fixture
def db_cursor(db_connection):
pass
def test_cursor_ready(db_cursor):
pass
Solution
import pytest
@pytest.fixture
def db_config():
return {'host': 'localhost', 'port': 5432}
@pytest.fixture
def db_connection(db_config):
return {'status': 'connected', 'host': db_config['host'], 'port': db_config['port']}
@pytest.fixture
def db_cursor(db_connection):
return {'query': None, 'connection': db_connection}
def test_cursor_ready(db_cursor):
assert db_cursor['query'] is None
assert db_cursor['connection']['status'] == 'connected'
assert db_cursor['connection']['host'] == 'localhost'