Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)?

like image 601
Hao Shen Avatar asked Feb 03 '17 19:02

Hao Shen


1 Answers

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
like image 110
andpei Avatar answered Sep 29 '22 08:09

andpei