夹具参考¶
另请参阅
另请参阅
内置夹具¶
夹具 使用 @pytest.fixture 装饰器定义。pytest 有几个有用的内置夹具
capfd
以文本形式捕获输出到文件描述符
1
和2
的内容。capfdbinary
以字节形式捕获输出到文件描述符
1
和2
的内容。caplog
控制日志记录和访问日志条目。
capsys
以文本形式捕获输出到
sys.stdout
和sys.stderr
的内容。capteesys
以与
capsys
相同的方式捕获,但也会根据--capture=
传递文本。capsysbinary
以字节形式捕获输出到
sys.stdout
和sys.stderr
的内容。cache
在 pytest 运行之间存储和检索值。
doctest_namespace
提供一个注入到 doctests 命名空间的字典。
monkeypatch
临时修改类、函数、字典、
os.environ
和其他对象。pytestconfig
访问配置值、插件管理器和插件钩子。
record_property
向测试添加额外属性。
record_testsuite_property
向测试套件添加额外属性。
recwarn
记录测试函数发出的警告。
request
提供有关正在执行的测试函数的信息。
testdir
提供一个临时测试目录,以帮助运行和测试 pytest 插件。
tmp_path
为每个测试函数提供一个唯一的临时目录的
pathlib.Path
对象。tmp_path_factory
创建会话范围的临时目录并返回
pathlib.Path
对象。tmpdir
为每个测试函数提供一个唯一的临时目录的 py.path.local 对象;已被
tmp_path
替换。tmpdir_factory
创建会话范围的临时目录并返回
py.path.local
对象;已被tmp_path_factory
替换。
夹具可用性¶
夹具的可用性是从测试的角度确定的。只有当夹具定义在测试所在的范围时,测试才能请求该夹具。如果夹具定义在类内部,则只能由该类内部的测试请求。但如果夹具定义在模块的全局范围,则该模块中的每个测试(即使是定义在类内部的测试)都可以请求它。
同样,只有当测试与 autouse 夹具定义在相同范围时,该测试才会被 autouse 夹具影响(参见 autouse 夹具在其作用域内首先执行)。
夹具也可以请求任何其他夹具,无论它们定义在哪里,只要请求它们的测试能够看到所有相关的夹具。
例如,这里有一个测试文件,其中包含一个夹具 (outer
),它请求了一个不在其定义范围内的夹具 (inner
)
from __future__ import annotations
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def outer(order, inner):
order.append("outer")
class TestOne:
@pytest.fixture
def inner(self, order):
order.append("one")
def test_order(self, order, outer):
assert order == ["one", "outer"]
class TestTwo:
@pytest.fixture
def inner(self, order):
order.append("two")
def test_order(self, order, outer):
assert order == ["two", "outer"]
从测试的角度来看,它们可以毫无问题地看到它们所依赖的每个夹具
因此,当它们运行时,outer
将毫无问题地找到 inner
,因为 pytest 是从测试的角度进行搜索的。
注意
夹具定义的范围对其实例化顺序没有影响:顺序由 此处 描述的逻辑决定。
conftest.py
: 在多个文件之间共享夹具¶
conftest.py
文件是为整个目录提供夹具的一种方式。定义在 conftest.py
中的夹具可以被该包中的任何测试使用,而无需导入它们(pytest 将自动发现它们)。
你可以有多个包含测试的嵌套目录/包,每个目录都可以有自己的 conftest.py
和自己的夹具,这些夹具会添加到父目录中 conftest.py
文件提供的夹具之上。
例如,给定如下测试文件结构
tests/
__init__.py
conftest.py
# content of tests/conftest.py
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def top(order, innermost):
order.append("top")
test_top.py
# content of tests/test_top.py
import pytest
@pytest.fixture
def innermost(order):
order.append("innermost top")
def test_order(order, top):
assert order == ["innermost top", "top"]
subpackage/
__init__.py
conftest.py
# content of tests/subpackage/conftest.py
import pytest
@pytest.fixture
def mid(order):
order.append("mid subpackage")
test_subpackage.py
# content of tests/subpackage/test_subpackage.py
import pytest
@pytest.fixture
def innermost(order, mid):
order.append("innermost subpackage")
def test_order(order, top):
assert order == ["mid subpackage", "innermost subpackage", "top"]
作用域的边界可以这样可视化
这些目录成为它们自己的一种作用域,其中定义在该目录的 conftest.py
文件中的夹具在该整个作用域内可用。
测试允许向上(走出圆圈)搜索夹具,但永远不能向下(走进圆圈)继续搜索。因此 tests/subpackage/test_subpackage.py::test_order
能够找到定义在 tests/subpackage/test_subpackage.py
中的 innermost
夹具,但是定义在 tests/test_top.py
中的夹具对其不可用,因为它必须向下(走进圆圈)一层才能找到它。
测试找到的第一个夹具就是将要使用的夹具,因此如果你需要更改或扩展某个夹具在特定作用域中的功能,夹具可以被覆盖。
你还可以使用 conftest.py
文件来实现 每个目录的本地插件。
来自第三方插件的夹具¶
然而,夹具不必以这种结构定义才能对测试可用。它们也可以由已安装的第三方插件提供,许多 pytest 插件就是这样运作的。只要这些插件已安装,它们提供的夹具就可以从测试套件中的任何位置请求。
由于它们是从测试套件结构外部提供的,第三方插件并不能像 conftest.py
文件和测试套件中的目录那样提供作用域。因此,pytest 将按照之前解释的方式,逐步退出作用域搜索夹具,只有在 最后 才会搜索插件中定义的夹具。
例如,给定以下文件结构
tests/
__init__.py
conftest.py
# content of tests/conftest.py
import pytest
@pytest.fixture
def order():
return []
subpackage/
__init__.py
conftest.py
# content of tests/subpackage/conftest.py
import pytest
@pytest.fixture(autouse=True)
def mid(order, b_fix):
order.append("mid subpackage")
test_subpackage.py
# content of tests/subpackage/test_subpackage.py
import pytest
@pytest.fixture
def inner(order, mid, a_fix):
order.append("inner subpackage")
def test_order(order, inner):
assert order == ["b_fix", "mid subpackage", "a_fix", "inner subpackage"]
如果 plugin_a
已安装并提供夹具 a_fix
,并且 plugin_b
已安装并提供夹具 b_fix
,那么测试搜索夹具的方式将如下所示
pytest 会首先在 tests/
内部的作用域中搜索 a_fix
和 b_fix
,然后才会在插件中搜索它们。
夹具实例化顺序¶
当 pytest 想要执行测试时,一旦它知道哪些夹具将被执行,它就必须确定它们的执行顺序。为此,它考虑了 3 个因素:
作用域
依赖关系
自动使用
夹具或测试的名称、它们的定义位置、它们的定义顺序以及夹具的请求顺序,除了巧合之外,对执行顺序没有影响。虽然 pytest 会尽量确保这些巧合在不同运行之间保持一致,但这不应作为依赖的依据。如果你想控制顺序,最安全的方法是依赖这 3 个因素并确保明确建立依赖关系。
高作用域夹具优先执行¶
在函数请求夹具时,高作用域(如 session
)的夹具会在低作用域(如 function
或 class
)的夹具之前执行。
这里是一个例子
from __future__ import annotations
import pytest
@pytest.fixture(scope="session")
def order():
return []
@pytest.fixture
def func(order):
order.append("function")
@pytest.fixture(scope="class")
def cls(order):
order.append("class")
@pytest.fixture(scope="module")
def mod(order):
order.append("module")
@pytest.fixture(scope="package")
def pack(order):
order.append("package")
@pytest.fixture(scope="session")
def sess(order):
order.append("session")
class TestClass:
def test_order(self, func, cls, mod, pack, sess, order):
assert order == ["session", "package", "module", "class", "function"]
测试将通过,因为作用域更大的夹具会首先执行。
顺序分解如下
相同顺序的夹具根据依赖关系执行¶
当一个夹具请求另一个夹具时,被请求的夹具会首先执行。因此,如果夹具 a
请求夹具 b
,则夹具 b
会首先执行,因为 a
依赖于 b
,没有它就无法操作。即使 a
不需要 b
的结果,如果它需要确保在 b
之后执行,它仍然可以请求 b
。
例如
from __future__ import annotations
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def a(order):
order.append("a")
@pytest.fixture
def b(a, order):
order.append("b")
@pytest.fixture
def c(b, order):
order.append("c")
@pytest.fixture
def d(c, b, order):
order.append("d")
@pytest.fixture
def e(d, b, order):
order.append("e")
@pytest.fixture
def f(e, order):
order.append("f")
@pytest.fixture
def g(f, c, order):
order.append("g")
def test_order(g, order):
assert order == ["a", "b", "c", "d", "e", "f", "g"]
如果我们绘制出依赖关系图,会得到这样的结果
每个夹具提供的规则(关于每个夹具必须在哪些夹具之后执行)足够全面,可以将其扁平化为如下所示
必须通过这些请求提供足够的信息,以便 pytest 能够确定一个清晰的线性依赖链,从而确定给定测试的操作顺序。如果存在任何歧义,并且操作顺序可以有多种解释,你应该假设 pytest 在任何时候都可能选择其中一种解释。
例如,如果 d
没有请求 c
,即图会是这样的
因为除了 g
之外,没有其他东西请求 c
,而 g
也请求 f
,现在不清楚 c
应该在 f
、e
或 d
之前还是之后。为 c
设置的唯一规则是它必须在 b
之后和 g
之前执行。
pytest 不知道在这种情况下 c
应该放在哪里,所以应该假设它可以放在 g
和 b
之间的任何位置。
这不一定是坏事,但需要牢记。如果它们的执行顺序可能影响测试所针对的行为,或者可能影响测试结果,那么应该以允许 pytest 线性化/“扁平化”该顺序的方式明确定义顺序。
Autouse 夹具在其作用域内首先执行¶
Autouse 夹具被假定适用于所有可以引用它们的测试,因此它们在该作用域中的其他夹具之前执行。被 autouse 夹具请求的夹具实际上也变成了 autouse 夹具,适用于真实 autouse 夹具所应用的测试。
因此,如果夹具 a
是 autouse 夹具,而夹具 b
不是,但夹具 a
请求夹具 b
,那么夹具 b
实际上也会成为一个 autouse 夹具,但仅限于 a
所应用的测试。
在上一个例子中,如果 d
没有请求 c
,图就会变得不清晰。但如果 c
是 autouse 夹具,那么 b
和 a
也将实际上成为 autouse 夹具,因为 c
依赖于它们。结果是,它们都将被移到该作用域内非 autouse 夹具的上方。
因此,如果测试文件看起来像这样
from __future__ import annotations
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def a(order):
order.append("a")
@pytest.fixture
def b(a, order):
order.append("b")
@pytest.fixture(autouse=True)
def c(b, order):
order.append("c")
@pytest.fixture
def d(b, order):
order.append("d")
@pytest.fixture
def e(d, order):
order.append("e")
@pytest.fixture
def f(e, order):
order.append("f")
@pytest.fixture
def g(f, c, order):
order.append("g")
def test_order_and_g(g, order):
assert order == ["a", "b", "c", "d", "e", "f", "g"]
图会是这样的
因为 c
现在可以在图中置于 d
的上方,pytest 可以再次将图线性化为如下所示
在这个例子中,c
也使 b
和 a
实际上成为 autouse 夹具。
不过,使用 autouse 要小心,因为 autouse 夹具会自动为每个能够访问它的测试执行,即使它们没有请求它。例如,考虑这个文件
from __future__ import annotations
import pytest
@pytest.fixture(scope="class")
def order():
return []
@pytest.fixture(scope="class", autouse=True)
def c1(order):
order.append("c1")
@pytest.fixture(scope="class")
def c2(order):
order.append("c2")
@pytest.fixture(scope="class")
def c3(order, c1):
order.append("c3")
class TestClassWithC1Request:
def test_order(self, order, c1, c3):
assert order == ["c1", "c3"]
class TestClassWithoutC1Request:
def test_order(self, order, c2):
assert order == ["c1", "c2"]
尽管 TestClassWithoutC1Request
中没有任何内容请求 c1
,但它仍然会在其中包含的测试中执行。
但是,仅仅因为一个 autouse 夹具请求了一个非 autouse 夹具,并不意味着该非 autouse 夹具就成了所有可应用它的上下文的 autouse 夹具。它只在真实 autouse 夹具(请求该非 autouse 夹具的夹具)可应用的上下文中才有效地成为 autouse 夹具。
例如,看一下这个测试文件
from __future__ import annotations
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def c1(order):
order.append("c1")
@pytest.fixture
def c2(order):
order.append("c2")
class TestClassWithAutouse:
@pytest.fixture(autouse=True)
def c3(self, order, c2):
order.append("c3")
def test_req(self, order, c1):
assert order == ["c2", "c3", "c1"]
def test_no_req(self, order):
assert order == ["c2", "c3"]
class TestClassWithoutAutouse:
def test_req(self, order, c1):
assert order == ["c1"]
def test_no_req(self, order):
assert order == []
它会分解成这样
对于 TestClassWithAutouse
内部的 test_req
和 test_no_req
,c3
有效地使 c2
成为一个 autouse 夹具,这就是为什么 c2
和 c3
即使没有被请求,也会为这两个测试执行的原因,也是为什么 c2
和 c3
在 test_req
之前执行 c1
的原因。
如果这使得 c2
成为一个 真正 的 autouse 夹具,那么 c2
也将为 TestClassWithoutAutouse
内部的测试执行,因为它们如果愿意的话可以引用 c2
。但它不会,因为从 TestClassWithoutAutouse
测试的角度来看,c2
不是一个 autouse 夹具,因为它们看不到 c3
。