The Room of Requirement at Hogwarts manifests whatever the seeker truly needs. pytest fixtures work the same way: they set up and provide objects that your tests require ā wands, wizards, potions, database connections ā anything.
A fixture is simply a function decorated with @pytest.fixture that returns (or yields) an object your test can use.
import pytest
class Wand:
def __init__(self, wood: str, core: str, length_inches: float):
self.wood = wood
self.core = core
self.length_inches = length_inches
def cast(self, spell: str) -> str:
return f"{spell}!"
@pytest.fixture
def elder_wand():
"""The Deathstick, the Wand of Destiny."""
return Wand(wood="elder", core="thestral_tail_hair", length_inches=15.0)
def test_elder_wand_properties(elder_wand):
assert elder_wand.wood == "elder"
assert elder_wand.core == "thestral_tail_hair"
def test_elder_wand_casts_spell(elder_wand):
result = elder_wand.cast("Expelliarmus")
assert result == "Expelliarmus!"
pytest sees elder_wand in the test signature, finds the matching fixture, calls it, and injects the result. No manual setup needed!
class Wizard:
def __init__(self, name: str, house: str, wand: Wand):
self.name = name
self.house = house
self.wand = wand
self.spells_cast = []
def cast(self, spell: str) -> str:
result = self.wand.cast(spell)
self.spells_cast.append(spell)
return result
@pytest.fixture
def harry_wand():
return Wand(wood="holly", core="phoenix_feather", length_inches=11.0)
@pytest.fixture
def harry_potter(harry_wand):
"""Fixtures can depend on other fixtures ā like The Chosen One needs his wand."""
return Wizard(name="Harry Potter", house="Gryffindor", wand=harry_wand)
def test_harry_can_cast_patronus(harry_potter):
result = harry_potter.cast("Expecto Patronum")
assert result == "Expecto Patronum!"
assert "Expecto Patronum" in harry_potter.spells_cast
By default, fixtures are function-scoped: created fresh for each test. But some resources are expensive (like brewing Polyjuice Potion ā it takes a month!). Use scope to control lifetime:
class PotionsCauldron:
"""Expensive to set up ā like Snape's classroom."""
brew_count = 0
def __init__(self):
PotionsCauldron.brew_count += 1
self.contents = None
def brew(self, potion_name: str):
self.contents = potion_name
return self.contents
# function scope (default): new cauldron per test
@pytest.fixture(scope="function")
def disposable_cauldron():
return PotionsCauldron()
# session scope: ONE cauldron for the ENTIRE test session
@pytest.fixture(scope="session")
def shared_cauldron():
"""Like the Room of Requirement ā created once, used by everyone."""
return PotionsCauldron()
# module scope: one per test file
@pytest.fixture(scope="module")
def module_cauldron():
return PotionsCauldron()
# class scope: one per test class
@pytest.fixture(scope="class")
def class_cauldron():
return PotionsCauldron()
def test_brew_polyjuice(shared_cauldron):
assert shared_cauldron.brew("Polyjuice") == "Polyjuice"
def test_brew_veritaserum(shared_cauldron):
# Same cauldron instance as above ā no re-creation!
assert shared_cauldron.brew("Veritaserum") == "Veritaserum"
| Scope | Created | Destroyed | Use Case |
|---|---|---|---|
function | Before each test | After each test | Cheap, isolated objects |
class | Once per test class | After class finishes | Shared within a class |
module | Once per .py file | After file finishes | DB connections per file |
session | Once per test run | After all tests | Expensive shared resources |
@pytest.fixture
def silence_classroom():
"""Cast Silencio on the classroom ā no return value needed."""
print("\nš Silencio! Classroom is quiet.")
# setup happens just by running the fixture
@pytest.mark.usefixtures("silence_classroom")
class TestPotionsExam:
"""All tests in this class get a silent classroom,
but don't need the fixture's return value."""
def test_brew_sleeping_draught(self):
potion = brew("Sleeping Draught")
assert potion.effect == "sleep"
def test_brew_antidote(self):
potion = brew("Antidote to Common Poisons")
assert potion.cures == "common_poisons"
@pytest.mark.usefixtures triggers the fixture without injecting its return value into the test. Perfect for side-effect-only fixtures (logging, DB seeding, environment setup).