pythonpytestfixture

In pytest, how to make sure the module level fixture is called before class level


My pytest is old(version 2.8.3).

pytestmark = pytest.mark.usefixtures("module_level")

@pytest.fixture(scope='module')
def module_level(request):
    print("module start")
    def fin():
        print("module end")
    request.addfinalizer(fin)

@pytest.fixture(scope='class')
def class_level(request):
    print("class start")
    def fin():
        print("class end")
    request.addfinalizer(fin)

@pytest.mark.usefixtures("class_level")
class TestMyClass:  
    def test_func(self):
        pass

However, the order I got is:

class start
module start
class end
module end

which is not what I want. So what's the right way to write a module level setup/cleanup fixture (and make sure it does the setup before everything and clean up after everything)?


Solution

  • pytest process the tests in the order they are written in a test file and loads the fixtures in the sequence as they are needed. See the documentation about fixtures.

    In your example, you pytest starts first with a class test, and the module dependency is loaded afterwards.

    There are multiple possibilities if you want to setup the "module" first

    a) module_level is used as a parameter by class_level and therefore loaded in front (pytestmark is not needed)

    import pytest
    
    @pytest.fixture(scope='module')
    def module_level(request):
        print("module start")
        def fin():
            print("module end")
        request.addfinalizer(fin)
    
    @pytest.fixture(scope='class')
    def class_level(request, module_level):
        print("class start")
        def fin():
            print("class end")
        request.addfinalizer(fin)
    
    @pytest.mark.usefixtures("class_level")
    class TestMyClass:
        def test_func(self):
            pass
    

    b) using @pytestmark as as requirement for the class-test and therefore it is loaded in front

    import pytest
    
    pytestmark = pytest.mark.usefixtures("module_level")
    
    @pytest.fixture(scope='module')
    def module_level(request):
        print("module start")
        def fin():
            print("module end")
        request.addfinalizer(fin)
    
    @pytest.fixture(scope='class')
    def class_level(request):
        print("class start")
        def fin():
            print("class end")
        request.addfinalizer(fin)
    
    @pytest.mark.usefixtures("class_level")
    @pytestmark
    class TestMyClass:
        def test_func(self):
            pass
    

    c) setup a function-test in front a class-test, this will execute module_level first

    import pytest
    
    pytestmark = pytest.mark.usefixtures("module_level")
    
    @pytest.fixture(scope='module')
    def module_level(request):
        print("module start")
        def fin():
            print("module end")
        request.addfinalizer(fin)
    
    @pytest.fixture(scope='class')
    def class_level(request):
        print("class start")
        def fin():
            print("class end")
        request.addfinalizer(fin)
    
    def test_case():
        assert True
    
    @pytest.mark.usefixtures("class_level")
    class TestMyClass:
        def test_func(self):
            pass
    

    this loads "module" in front of "class" in each case (pytest-2.8.7):

    a)

    ::TestMyClass::test_func module start
    class start
    class end
    module end
    

    b)

    ::TestMyClass::test_func module start
    class start
    class end
    module end
    

    c)

    ::test_case module start
    ::TestMyClass::test_func class start
    class end
    module end