fixture 1
Starting with the first one, this creates a plain-data fixture. The mock is (imo misleadingly) only alive for the duration of the fixture function because it uses return
.
In order ~roughly what happens for that:
- pytest notices your fixture is used for the test function
- it calls the fixture function
- the mock decorator starts the patch
- the mock decorator calls your actual function (which returns a value)
- the mock decorator undoes the patch
- pytest notices it wasn't a generator and so that's the value of your fixture
fixture 2
the second is identical in behaviour to the first, except it uses the context manager form of mock
instead of the decorator. personally I don't like the decorator form but that's just me :D
fixture 3
(first before I continue, pytest.yield_fixture
is a deprecated alias for pytest.fixture
-- you can just use @pytest.fixture
)
The third does something different! The patch is alive for the duration of the test because it has "yielded" during the fixture. This is a kind of way to create a setup + teardown fixture all in one. Here's roughly the execution here
- pytest notices your fixture is used for the test function
- pytest calls the fixture function
- since it is a generator, it returns immediately without executing code
- pytest notices it is a generator, calls
next(...)
on it
- this causes the code to execute until the
yield
and then "pausing". you can think of it kind of as a co-routine
- the
__enter__
of the mock
is called making the patch active
- the value that is
yield
ed is used as the value of the fixture
- pytest then executes your test function
- pytest then calls
next(...)
again on the generator to exhaust the fixture
- this
__exit__
s the with statement, undoing the patch
which to choose?
the best answer is it depends. Since 1 and 2 are functionally equivalent it's up to personal preference. Pick 3. if you need the patch to be active during the entire duration of your test. And don't use pytest.yield_fixture
, just use pytest.fixture
.