Defining the scope of dependencies

In the previous examples, we didn’t specify a scope for the dependencies. All dependencies were taken in module scope, which is the default. As a consequence, tests were constraint to depend only from other tests in the same test module.

The pytest.mark.dependency() marker as well as the pytest_dependency.depends() function take an optional scope argument. Possible values are ‘session’, ‘package’, ‘module’, or ‘class’.

New in version 0.5.0: the scope of dependencies has been introduced. In earlier versions, all dependencies were implicitly in module scope.

Explicitely specifying the scope

The default value for the scope argument is ‘module’. Thus, the very first example from Section Basic usage could also be written as:

import pytest

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_a():
    assert False

@pytest.mark.dependency()
def test_b():
    pass

@pytest.mark.dependency(depends=["test_a"], scope='module')
def test_c():
    pass

@pytest.mark.dependency(depends=["test_b"], scope='module')
def test_d():
    pass

@pytest.mark.dependency(depends=["test_b", "test_c"], scope='module')
def test_e():
    pass

It works exactly the same. The only difference is that the default scope has been made explicit.

Dependencies in session scope

If a test depends on another test in a different test module, the dependency must either be in session or package scope. Consider the following two test modules:

# test_mod_01.py

import pytest

@pytest.mark.dependency()
def test_a():
    pass

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_b():
    assert False

@pytest.mark.dependency(depends=["test_a"])
def test_c():
    pass


class TestClass(object):

    @pytest.mark.dependency()
    def test_b(self):
        pass

and

# test_mod_02.py

import pytest

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_a():
    assert False

@pytest.mark.dependency(
    depends=["tests/test_mod_01.py::test_a", "tests/test_mod_01.py::test_c"],
    scope='session'
)
def test_e():
    pass

@pytest.mark.dependency(
    depends=["tests/test_mod_01.py::test_b", "tests/test_mod_02.py::test_e"],
    scope='session'
)
def test_f():
    pass

@pytest.mark.dependency(
    depends=["tests/test_mod_01.py::TestClass::test_b"],
    scope='session'
)
def test_g():
    pass

Let’s assume the modules to be stored as tests/test_mod_01.py and tests/test_mod_02.py relative to the current working directory respectively. The test test_e in tests/test_mod_02.py will be run and succeed. It depends on test_a and test_c in tests/test_mod_01.py that both succeed. It does not matter that there is another test_a in tests/test_mod_02.py that fails. Test test_f in tests/test_mod_02.py will be skipped, because it depends on test_b in tests/test_mod_01.py that fails. Test test_g in turn will be run and succeed. It depends on the test method test_b of class TestClass in tests/test_mod_01.py, not on the test function of the same name.

The scope argument only affects the references in the depends argument of the marker. It does not matter which scope is set for the dependencies: the dependency of test_e in tests/test_mod_02.py on test_a in tests/test_mod_01.py is in session scope. It is not needed to set the scope also for test_a.

Note that the references in session scope must use the full node id of the dependencies. This node id is composed of the module path, the name of the test class if applicable, and the name of the test, separated by a double colon “::”, see Section Names for details. References in module scope on the other hand must omit the module path in the node id, because that is implied by the scope.

Package scope is only available if the test is in a package and then restricts the dependencies to tests within the same package. Otherwise it works the same as session scope.

The class scope

Test dependencies may also be in class scope. This is only available for methods of a test class and restricts the dependencies to other test methods of the same class.

Consider the following example:

import pytest

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_a():
    assert False


class TestClass1(object):

    @pytest.mark.dependency()
    def test_b(self):
        pass


class TestClass2(object):

    @pytest.mark.dependency()
    def test_a(self):
        pass

    @pytest.mark.dependency(depends=["test_a"])
    def test_c(self):
        pass

    @pytest.mark.dependency(depends=["test_a"], scope='class')
    def test_d(self):
        pass

    @pytest.mark.dependency(depends=["test_b"], scope='class')
    def test_e(self):
        pass

The test method test_c of class TestClass2 will be skipped because it depends on test_a. The marker does not have a scope argument, so this dependency defaults to module scope. The dependency thus resolves to the function test_a at module level, which failed. The fact that there is also a method test_a in this class does not matter, because that would need to be referenced as TestClass2::test_a in module scope. The test method test_d of class TestClass2 depends on test_a in class scope. This resolves to the method test_a of TestClass2 which succeeds. As a result, test_d will be run and succeed as well. Test method test_e of class TestClass2 will be skipped, because it depends on test_b in class scope, but there is no method by that name in this class. The fact that there is another class TestClass1 having a method by that name is irrelevant.