Reputation: 6639
I'm using Python 3 with PyTest and have defined a number of fixture objects in conftest.py
. The problem I'm having is that there are some fixture objects that will be needed by every test case. Requesting these fixture references in all test cases results in a lot of repeated boilerplate code.
Here's the fixture in conftest.py
:
def fixtureA(request):
_fixture = FixtureA()
# initialize the fixture, do stuff with request
return _fixture
Here's the current test class, where I want to remove all fixtures from arguments for readability:
class TestSomeFeature(BaseTest):
def test_some_function(self, fixtureA, fixtureB, fixtureC):
fixtureA.doSomething()
# execute test case, do stuff with other fixtures
def test_some_other_function(self, fixtureA, fixtureB, fixtureC):
data = fixtureB.getData()
# execute test case
This approach works, but I'd like to find a way to use dependency injection (or similar) to automagically inject the fixtures in BaseTest
attributes without having to specify them in every test case's argument list. I'm looking for something like this, but open to any other suggestions:
class BaseTest:
# This does not work, since pytest does not allow constructors in the test class
def __init__(fixtureA, fixtureB, fixtureC):
self.fixtureA = fixtureA
self.fixtureB = fixtureB
self.fixtureC = fixtureC
I want the test class to look like this, much cleaner!
class TestSomeFeature(BaseTest):
def test_some_function(self):
self.FixtureA.doSomething()
# execute test case
def test_some_other_function(self):
data = self.FixtureB.getData()
# execute test case
Upvotes: 2
Views: 7404
Reputation: 6639
The following code is based on the answer from @MrBeanBremen.
You can create a fixture in the base class called injector
, whose sole responsibility it is to inject fixtures from conftest
into the base class:
class BaseTest:
@fixture(autouse=True)
# noinspection PyAttributeOutsideInit
def injector(self, fixtureA, fixtureB):
self.fixtureA = fixtureA
self.fixtureB = fixtureB
All test classes that inherit from BaseTest
can now access the fixtures without any boilerplate code. A constructor cannot be used in BaseTest
, since PyTest will ignore the test class completely if a constructor is implemented. PyCharm will generate a weak warning, since we're defining attributes outside __init__
, but that can be suppressed using the noinspection
comment.
Upvotes: 2
Reputation: 16825
First, you can define fixtures both in conftest.py
and in test classes. The difference is visibility: if you define a fixture in a conftest.py
, it is visible to all tests on the level of that conftest.py
file and below. If you define it inside a test module, it is visible in this module only. If you define it inside a test class, it is visible in this class and derived classes.
Also note that you can use autotest=True
also if you return a value - you just have to reference the fixture in the respective tests. You can also save the fixture value in a variable. Here is a simplistic example for both cases if you are are using a base class:
class TestBase:
@pytest.fixture(autouse=True)
def fixture1(self):
self.value1 = 1 # save the fixture value
yield
@pytest.fixture
def fixture2(self):
yield 2 # return the fixture value - fixtue has to be referenced
# autouse can still make sense if there is setup/tearDown code,
# and the fixture must not be referenced in all of the tests
@pytest.fixture(autouse=True)
def fixture3(self):
self.value3 = 3
yield 3 # do both - can be used either way
class TestDerived(TestBase):
def test_1(self):
assert self.value1 == 1
def test_2(self, fixture2):
assert fixture2 == 2
def test_3_1(self):
assert self.value3 == 3
def test_3_2(self, fixture3):
assert fixture3 == 3
Note that you get the fixture value, not the fixture itself, if you refer to the fixture, so there is no need (and it is not possible) to call the fixture - instead you directly use the value returned by the fixture.
Upvotes: 4