You’ve already seen that tmpdir uses tmpdir_factory. And you used tmpdir in our tasks_db fixture. Let’s keep the chain going and add some specialized fixtures for non-empty tasks databases:
| @pytest.fixture() |
| def db_with_3_tasks(tasks_db, tasks_just_a_few): |
| """Connected db with 3 tasks, all unique.""" |
| for t in tasks_just_a_few: |
| tasks.add(t) |
| |
| |
| @pytest.fixture() |
| def db_with_multi_per_owner(tasks_db, tasks_mult_per_owner): |
| """Connected db with 9 tasks, 3 owners, all with 3 tasks.""" |
| for t in tasks_mult_per_owner: |
| tasks.add(t) |
These fixtures all include two fixtures each in their parameter list: tasks_db and a data set. The data set is used to add tasks to the database. Now tests can use these when you want the test to start from a non-empty database, like this:
| def test_add_increases_count(db_with_3_tasks): |
| """Test tasks.add() affect on tasks.count().""" |
| # GIVEN a db with 3 tasks |
| # WHEN another task is added |
| tasks.add(Task('throw a party')) |
| |
| # THEN the count increases by 1 |
| assert tasks.count() == 4 |
This also demonstrates one of the great reasons to use fixtures: to focus the test on what you’re actually testing, not on what you had to do to get ready for the test. I like using comments for GIVEN/WHEN/THEN and trying to push as much GIVEN into fixtures for two reasons. First, it makes the test more readable and, therefore, more maintainable. Second, an assert or exception in the fixture results in an ERROR, while an assert or exception in a test function results in a FAIL. I don’t want test_add_increases_count() to FAIL if database initialization failed. That would just be confusing. I want a FAIL for test_add_increases_count() to only be possible if add() really failed to alter the count. Let’s trace it and see all the fixtures run:
| $ cd /path/to/code/ch3/a/tasks_proj/tests/func |
| $ pytest --setup-show test_add.py::test_add_increases_count |
| ===================== test session starts ====================== |
| collected 1 item |
| |
| test_add.py |
| SETUP S tmpdir_factory |
| SETUP F tmpdir (fixtures used: tmpdir_factory) |
| SETUP F tasks_db (fixtures used: tmpdir) |
| SETUP F tasks_just_a_few |
| SETUP F db_with_3_tasks (fixtures used: tasks_db, tasks_just_a_few) |
| func/test_add.py::test_add_increases_count |
| (fixtures used: db_with_3_tasks, tasks_db, tasks_just_a_few, |
| tmpdir, tmpdir_factory). |
| TEARDOWN F db_with_3_tasks |
| TEARDOWN F tasks_just_a_few |
| TEARDOWN F tasks_db |
| TEARDOWN F tmpdir |
| TEARDOWN S tmpdir_factory |
| |
| =================== 1 passed in 0.04 seconds =================== |
There are those F’s and S’s for function and session scope again. Let’s learn about those next.