Fixtures
A fixture builds something tests need. Mark a function with @pytest.fixture, and any test that names it as a parameter receives its return value. pytest calls the fixture again for every test, so one test cannot leave changes behind for the next.
import pytest
@pytest.fixture
def basket():
return ["tea"]
def test_add(basket):
basket.append("jam")
assert basket == ["tea", "jam"]
def test_fresh(basket):
assert basket == ["tea"]
# pytest calls basket() for each test and
# passes the result in, like this:
test_add(["tea"])
test_fresh(["tea"])
print("both pass, in any order")Building on fixtures
A fixture can take other fixtures as parameters, so a special setup reuses the plain one. If it uses yield instead of return, the code after yield runs once the test is over, pass or fail. That is where files get closed and connections dropped.
A shared module-level object is the usual alternative, and the usual bug: a test that changes it breaks whichever test runs next.
Your task
Account is written for you. Write a fixture account returning a fresh Account(100) and a fixture rich_account that takes account, deposits 1000 and returns it. Then write test_deposit (deposit 50, expect 150) and test_withdraw (withdraw 30, expect 70) using account, and test_large_withdrawal (withdraw 900, expect 200) using rich_account.
The explanation opens here once every check passes.