When is it appropriate to use a Pytest fixture versus an instance variable?
Suppose I have a large expected JSON output stored locally that several tests employ. Which of the following approaches is more appropriate? I can see that the state of the object remains unchanged from test to test.
@pytest.fixture
def expected_output():
return <fixture>
def test_1(expected_output):
<do something>
assert output == expected_output
def test_2(expected_output):
<do something>
assert output == expected_output
class SomeTestClass(TestCase):
def setUp(self):
self.expected_output = <fixture>
def test_1(self):
<do something>
self.assertEqual(output, self.expected_output)
def test_2(self):
<do something>
self.assertEqual(output, self.expected_output)
I've combed through the Pytest docs and don't seem to understand the distinction. Having instance variables seems to carry a lot of benefits of fixtures--i.e., sharing values across tests.
The first difference you have already shown in your example: a fixture does not need a test class.
If you use a test class, you can also use the setup
method in pytest
, no need to use unittest.TestCase
:
class SomeTestClass:
def setup_method(self):
self.expected_output = <fixture>
def test_1(self):
<do something>
assert output == self.expected_output
This is described as the classic xunit-style setup in pytest
. From that documentation:
While these setup/teardown methods are simple and familiar to those coming from a unittest or nose background, you may also consider using pytest’s more powerful fixture mechanism which leverages the concept of dependency injection, allowing for a more modular and more scalable approach for managing test state, especially for larger projects and for functional testing. You can mix both fixture mechanisms in the same file but test methods of
unittest.TestCase
subclasses cannot receive fixture arguments.
Generally I would advice against mixing unittest
and pytest
for new test code. There is nothing in unittest
that cannot be done with pytest, but you cannot run unittest
against a test that uses pytest
features.
In your concrete example, you actually don't want an instance variable, because the expected output does not change with the test. You could just declare the variable statically and use it (both in or outside of classes), or you could use a class variable (and use setup_class
to set it in pytest
).
For this trivial example it probably makes no real difference which concept to use, but if you you need a setup
and a teardown
, fixtures can be more helpfull, as they provide both:
@pytest.fixture
def some_resource():
resource = acquire_resource()
yield resource
free(resource)
In the end, there is no "appropriate" choice in the cases where you can use both variants, but using fixtures always feels more consistent - one mechanism instead of two different ones - and it is the pytest
way. Not the least factor - most people using pytest
use fixtures in these cases, so finding help and sharing code is easier if you use the same.