import pytest
@pytest.fixture()
def my_fixture():
data = {'x': 1, 'y': 2, 'z': 3}
return data
def test_my_fixture(my_fixture):
assert my_fixture['x'] == 1
What's the benefit of marking my_fixture
as a pytest fixture
? It seems like the fixture's benefit is the same with my_fixture
just being a normal function, removing the decorator.
I see the benefit only here where my_fixture
is run simply by providing it as a parameter to test_my_fixture
:
@pytest.fixture()
def my_fixture():
print "\nI'm the fixture"
def test_my_fixture(my_fixture):
print "I'm the test"
This should print:
I'm the fixture
I'm the test
Therefore, instead of running the same code for every test, we can attach fixture function to the tests and it will run and return the data to the test before executing each test. A test function can use a fixture by mentioning the fixture name as an input parameter.
One of the things that makes pytest’s fixture system so powerful, is that it gives us the ability to define a generic setup step that can be reused over and over, just like a normal function would be used. Two different tests can request the same fixture and have pytest give each test their own result from that fixture.
@pytest.mark.usefixtures("cleandir", "anotherfixture") def test(): ... and you may specify fixture usage at the test module level using pytestmark: It is also possible to put fixtures required by all tests in your project into an ini-file: Note this mark has no effect in fixture functions. For example, this will not work as expected:
Being able to reuse fixtures in parametrized tests is a must when we want to avoid repetition. Unfortunately, pytest doesn’t support that yet. On the other hand, we can make it happen either by using getfixturevalue in pytest or through a third-party library.
If you do not declare my_fixture
as a fixture, then the test will not be able to use it as a fixture:
import pytest
def my_fixture():
data = {'x': 1, 'y': 2, 'z': 3}
return data
def test_my_fixture(my_fixture):
assert my_fixture['x'] == 1
This script leads to an error:
def test_my_fixture(my_fixture):
E fixture 'my_fixture' not found
> available fixtures: cache, capfd, capsys, doctest_namespace, monkeypatch, pytestconfig, record_xml_property, recwarn, tmpdir, tmpdir_factory
> use 'pytest --fixtures [testpath]' for help on them.
The purpose of the fixtures is to prepare something before the test(s). And to kill it afterward. And NOT to be part of the test (e.g. in logging, reporting, ways of exception handling, etc). It is kind of a pre-condition, created by something else, and just given to the test.
If you just declare it as a function, and use it as a function, it is a function, not a fixture. And its failures become the failures of the test, not the general framework run.
Consider this example:
import pytest
@pytest.fixture()
def my_fixture():
data = {'x': 1, 'y': 2, 'z': 3}
print('prepare')
yield data
print('destroy')
def test_my_fixture(my_fixture):
print('test it')
assert my_fixture['x'] == 1
Also in this example try uncommenting the raising lines, and see the difference. It will be ERROR vs FAILURE. It is critical for how the test results are interpreted and handled later (e.g., by the developers, QA engineers, or whoever analyses the test results).
import pytest
@pytest.fixture()
def my_fixture():
print('prepare')
# raise Exception('Oops')
yield None
print('destroy')
def test_my_fixture(my_fixture):
# raise Exception('Booms!')
print('test it')
In the fixture:
======================================= test session starts ========================================
collected 1 item
t.py E
============================================== ERRORS ==============================================
________________________________ ERROR at setup of test_my_fixture _________________________________
@pytest.fixture()
def my_fixture():
data = {'x': 1, 'y': 2, 'z': 3}
print('prepare')
> raise Exception('Oops')
E Exception: Oops
t.py:7: Exception
===================================== 1 error in 0.03 seconds ======================================
In the test or any function called from the test:
======================================= test session starts ========================================
collected 1 item
t.py F
============================================= FAILURES =============================================
_________________________________________ test_my_fixture __________________________________________
my_fixture = {'x': 1, 'y': 2, 'z': 3}
def test_my_fixture(my_fixture):
> raise Exception('Booms!')
E Exception: Booms!
t.py:12: Exception
===================================== 1 failed in 0.03 seconds =====================================
Also note that the fixtures have scopes: session, module, function. It will be difficult to demonstrate in a short example, but in that case, the fixture will be prepared and destroyed only once (usually only once) for multiple tests within that scope. This is important for the heavy-weight fixtures, such as the database pre-population. Such fixtures usually reside in the conftest.py
pseudo-plugin in the directory structure (NB: this is not a regular module to be imported! by design, at least).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With