Skip to content

Commit 2952196

Browse files
committed
Add a section on scope of dependencies to the documentation.
1 parent 9a9dc17 commit 2952196

File tree

7 files changed

+200
-0
lines changed

7 files changed

+200
-0
lines changed

doc/examples/scope_class.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import pytest
2+
3+
@pytest.mark.dependency()
4+
@pytest.mark.xfail(reason="deliberate fail")
5+
def test_a():
6+
assert False
7+
8+
9+
class TestClass1(object):
10+
11+
@pytest.mark.dependency()
12+
def test_b(self):
13+
pass
14+
15+
16+
class TestClass2(object):
17+
18+
@pytest.mark.dependency()
19+
def test_a(self):
20+
pass
21+
22+
@pytest.mark.dependency(depends=["test_a"])
23+
def test_c(self):
24+
pass
25+
26+
@pytest.mark.dependency(depends=["test_a"], scope='class')
27+
def test_d(self):
28+
pass
29+
30+
@pytest.mark.dependency(depends=["test_b"], scope='class')
31+
def test_e(self):
32+
pass

doc/examples/scope_module.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import pytest
2+
3+
@pytest.mark.dependency()
4+
@pytest.mark.xfail(reason="deliberate fail")
5+
def test_a():
6+
assert False
7+
8+
@pytest.mark.dependency()
9+
def test_b():
10+
pass
11+
12+
@pytest.mark.dependency(depends=["test_a"], scope='module')
13+
def test_c():
14+
pass
15+
16+
@pytest.mark.dependency(depends=["test_b"], scope='module')
17+
def test_d():
18+
pass
19+
20+
@pytest.mark.dependency(depends=["test_b", "test_c"], scope='module')
21+
def test_e():
22+
pass

doc/examples/scope_session_mod_01.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# test_mod_01.py
2+
3+
import pytest
4+
5+
@pytest.mark.dependency()
6+
def test_a():
7+
pass
8+
9+
@pytest.mark.dependency()
10+
@pytest.mark.xfail(reason="deliberate fail")
11+
def test_b():
12+
assert False
13+
14+
@pytest.mark.dependency(depends=["test_a"])
15+
def test_c():
16+
pass
17+
18+
19+
class TestClass(object):
20+
21+
@pytest.mark.dependency()
22+
def test_b(self):
23+
pass

doc/examples/scope_session_mod_02.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# test_mod_02.py
2+
3+
import pytest
4+
5+
@pytest.mark.dependency()
6+
@pytest.mark.xfail(reason="deliberate fail")
7+
def test_a():
8+
assert False
9+
10+
@pytest.mark.dependency(
11+
depends=["tests/test_mod_01.py::test_a", "tests/test_mod_01.py::test_c"],
12+
scope='session'
13+
)
14+
def test_e():
15+
pass
16+
17+
@pytest.mark.dependency(
18+
depends=["tests/test_mod_01.py::test_b", "tests/test_mod_02.py::test_e"],
19+
scope='session'
20+
)
21+
def test_f():
22+
pass
23+
24+
@pytest.mark.dependency(
25+
depends=["tests/test_mod_01.py::TestClass::test_b"],
26+
scope='session'
27+
)
28+
def test_g():
29+
pass

doc/src/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Content of the documentation
1414
about
1515
install
1616
usage
17+
scope
1718
advanced
1819
names
1920
configuration

doc/src/scope.rst

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
Defining the scope of dependencies
2+
==================================
3+
4+
In the previous examples, we didn't specify a scope for the
5+
dependencies. All dependencies were taken in module scope, which is
6+
the default. As a consequence, tests were constraint to depend only
7+
from other tests in the same test module.
8+
9+
The :func:`pytest.mark.dependency` marker as well as the
10+
:func:`pytest_dependency.depends` function take an optional `scope`
11+
argument. Possible values are `'session'`, `'package'`, `'module'`,
12+
or `'class'`.
13+
14+
.. versionadded:: 0.5.0
15+
the scope of dependencies has been introduced. In earlier
16+
versions, all dependencies were implicitly in module scope.
17+
18+
19+
Explicitely specifying the scope
20+
--------------------------------
21+
22+
The default value for the `scope` argument is `'module'`. Thus, the
23+
very first example from Section :ref:`usage-basic` could also be
24+
written as:
25+
26+
.. literalinclude:: ../examples/scope_module.py
27+
28+
It works exactly the same. The only difference is that the default
29+
scope has been made explicit.
30+
31+
Dependencies in session scope
32+
-----------------------------
33+
34+
If a test depends on another test in a different test module, the
35+
dependency must either be in session or package scope. Consider the
36+
following two test modules:
37+
38+
.. literalinclude:: ../examples/scope_session_mod_01.py
39+
40+
and
41+
42+
.. literalinclude:: ../examples/scope_session_mod_02.py
43+
44+
Let's assume the modules to be stored as `tests/test_mod_01.py` and
45+
`tests/test_mod_02.py` relative to the current working directory
46+
respectively. The test `test_e` in `tests/test_mod_02.py` will be run
47+
and succeed. It depends on `test_a` and `test_c` in
48+
`tests/test_mod_01.py` that both succeed. It does not matter that
49+
there is another `test_a` in `tests/test_mod_02.py` that fails. Test
50+
`test_f` in `tests/test_mod_02.py` will be skipped, because it depends
51+
on `test_b` in `tests/test_mod_01.py` that fails. Test `test_g` in
52+
turn will be run and succeed. It depends on the test method `test_b`
53+
of class `TestClass` in `tests/test_mod_01.py`, not on the test
54+
function of the same name.
55+
56+
Note that the references in session scope must use the full node id of
57+
the dependencies. This node id is composed of the module path, the
58+
name of the test class if applicable, and the name of the test,
59+
separated by a double colon "::", see Section :ref:`names` for
60+
details. References in module scope on the other hand must omit the
61+
module path in the node id, because that is implied by the scope.
62+
63+
Package scope is only available if the test is in a package and then
64+
restricts the dependencies to tests within the same package.
65+
Otherwise it works the same as session scope.
66+
67+
The class scope
68+
---------------
69+
70+
Test dependencies may also be in class scope. This is only available
71+
for methods of a test class and restricts the dependencies to other
72+
test methods of the same class.
73+
74+
Consider the following example:
75+
76+
.. literalinclude:: ../examples/scope_class.py
77+
78+
The test method `test_c` of class `TestClass2` will be skipped because
79+
it depends on `test_a`. The marker does not have a `scope` argument,
80+
so this dependency defaults to module scope. The dependency thus
81+
resolves to the function `test_a` at module level, which failed. The
82+
fact that there is also a method `test_a` in this class does not
83+
matter, because that would need to be referenced as
84+
`TestClass2::test_a` in module scope. The test method `test_d` of
85+
class `TestClass2` depends on `test_a` in class scope. This resolves
86+
to the method `test_a` of `TestClass2` which succeeds. As a result,
87+
`test_d` will be run and succeed as well. Test method `test_e` of
88+
class `TestClass2` will be skipped, because it depends on `test_b` in
89+
class scope, but there is no method by that name in this class. The
90+
fact that there is another class `TestClass1` having a method by that
91+
name is irrelevant.

doc/src/usage.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Using pytest-dependency
33

44
The plugin defines a new marker :func:`pytest.mark.dependency`.
55

6+
.. _usage-basic:
7+
68
Basic usage
79
-----------
810

0 commit comments

Comments
 (0)