pythonpytestconftest

Have a object created on a fixture accassible at setup_class, teardown_class, setup_method, and teardown_method


I have a conftest.py:

import pytest
import asyncio
from some_library import MyLibrary

@pytest.fixture()
def event_loop(request):
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()
    
@pytest.fixture(name="library_instance", scope='session')
def fixture_library_instance():
    library = MyLibrary()
    
    print("\nSetup Library")
    library.config()
    
    yield library

    print("\nTeardown Library")
    library = None

And a test file (test_1.py):

import pytest

class Test_Somemore:
    @classmethod
    def setup_class(self):
        print("\nSetup Class")
        
    @classmethod
    def teardown_class(self):
        print("\nTeardown Class")

    @classmethod
    def setup_method(self, method):
        print("\nSetup Test = ", method.__name__)

    @classmethod
    def teardown_method(self, method):
        print("\nTeardown Test = ", method.__name__)

    @pytest.mark.usefixtures("library_instance")
    @pytest.mark.asyncio
    async def test_something_4(self, library_instance):
        print(f"\ntest 4 - {library_instance.var}")
        assert 1 == 1
        assert library_instance.var == 100
    
    @pytest.mark.usefixtures("library_instance")
    @pytest.mark.asyncio
    async def test_something_5(self, library_instance):
        print(f"\ntest 5 - {library_instance.var}")
        assert 2 == 2
        assert library_instance.var == 100

    @pytest.mark.usefixtures("library_instance")
    @pytest.mark.asyncio
    async def test_something_6(self, library_instance):
        print(f"\ntest 6 - {library_instance.var}")
        assert 3 == 3
        assert library_instance.var == 100

The order it is being called is:

  1. Setup Library
  2. Setup Class
  3. Setup Test (test 4)
  4. Teardown Test (test 4)
  5. Setup Test (test 5)
  6. Teardown Test (test 5)
  7. Setup Test (test 6)
  8. Teardown Test (test 6)
  9. Teardown Class
  10. Teardown Libary

This is ok.

What I need is the following:

  1. To have library_instance (from the fixture "fixture_library_instance") callable inside the setup_class, teardown_class, setup_method, and teardown_method. Not just the test cases. I haven't found a way to make this work.
  2. In teardown_method, check if the test has failed. If it did, I want to call some functions.

Basically something like this:

    @classmethod
    def setup_class(self):
        print("\nSetup Class")
        library_instance.foo1()
        
    @classmethod
    def teardown_class(self):
        print("\nTeardown Class")
        library_instance.foo2()

    @classmethod
    def setup_method(self, method):
        print("\nSetup Test = ", method.__name__)
        library_instance.foo3()

    @classmethod
    def teardown_method(self, method):
        print("\nTeardown Test = ", method.__name__)
        if test == FAIL:
            library_instance.foo4()

Can somebody please help me?


Solution

  • Both your requirements are possible.
    The first one (accessing another fixture in a setup method) is trivial if you change your setup/teardown methods to fixtures. In this case, you can just reference other fixtures of the same or a wider scope:

    class TestSomemore:
        @classmethod
        @pytest.fixture(scope="class", autouse=True)
        def prepare_class(cls, library_instance):
            print("\nSetup Class")
            library_instance.foo1()
            yield
            print("\nTeardown Class")
    
        @pytest.fixture(autouse=True)
        def prepare_method(self, request, library_instance):
            print("\nSetup Test = ", request.node.name)
            library_instance.foo2()
            yield
            print("\nTeardown Test = ", request.node.name)
    

    A few remarks:

    Using the fixture notation arguably is preferrable to separate setup/teardown methods, as it only needs one method, and also allows the use of the same local variables for both setup and teardown (e.g. before and after the yield), though that is also a matter of taste.

    As for the second part (checking if a fixture has failed) - this has been nicely answered here, with a link to the relevant documentation, so I won't repeat it here.