开始¶
安装 pytest¶
在命令行中运行以下命令
pip install -U pytest
检查你安装的版本是否正确
$ pytest --version
pytest 9.0.1
创建你的第一个测试¶
创建一个名为 test_sample.py 的新文件,包含一个函数和一个测试
# content of test_sample.py
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
该测试
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item
test_sample.py F [100%]
================================= FAILURES =================================
_______________________________ test_answer ________________________________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:6: AssertionError
========================= short test summary info ==========================
FAILED test_sample.py::test_answer - assert 4 == 5
============================ 1 failed in 0.12s =============================
[100%] 指的是运行所有测试用例的整体进度。完成后,pytest 会显示一个失败报告,因为 func(3) 没有返回 5。
注意
你可以使用 assert 语句来验证测试预期。pytest 的 高级断言内省 将智能地报告断言表达式的中间值,这样你就可以避免 JUnit 遗留方法的许多名称。
运行多个测试¶
pytest 将运行当前目录及其子目录中所有形式为 test_*.py 或 *_test.py 的文件。更一般地,它遵循 标准测试发现规则。
断言某个异常被抛出¶
使用 raises 助手断言某些代码会抛出异常
# content of test_sysexit.py
import pytest
def f():
raise SystemExit(1)
def test_mytest():
with pytest.raises(SystemExit):
f()
以“安静”报告模式执行测试函数
$ pytest -q test_sysexit.py
. [100%]
1 passed in 0.12s
注意
-q/--quiet 标志使本例和后续示例的输出保持简洁。
有关指定预期异常的更多详细信息,请参阅 关于近似相等的断言。
将多个测试分组到一个类中¶
一旦你开发了多个测试,你可能希望将它们分组到一个类中。pytest 可以轻松地创建一个包含多个测试的类
# content of test_class.py
class TestClass:
def test_one(self):
x = "this"
assert "h" in x
def test_two(self):
x = "hello"
assert hasattr(x, "check")
pytest 遵循其 Python 测试发现约定 来发现所有测试,因此它会找到所有以 test_ 为前缀的函数。无需继承任何东西,但请确保你的类以 Test 为前缀,否则该类将被跳过。我们可以简单地通过传递文件名来运行模块
$ pytest -q test_class.py
.F [100%]
================================= FAILURES =================================
____________________________ TestClass.test_two ____________________________
self = <test_class.TestClass object at 0xdeadbeef0001>
def test_two(self):
x = "hello"
> assert hasattr(x, "check")
E AssertionError: assert False
E + where False = hasattr('hello', 'check')
test_class.py:8: AssertionError
========================= short test summary info ==========================
FAILED test_class.py::TestClass::test_two - AssertionError: assert False
1 failed, 1 passed in 0.12s
第一个测试通过,第二个测试失败。你可以轻松地看到断言中的中间值,以帮助你理解失败的原因。
将测试分组到类中可能带来以下好处
测试组织
仅在该特定类中为测试共享夹具
在类级别应用标记并让它们隐式应用于所有测试
在类中分组测试时需要注意的一点是,每个测试都有该类的唯一实例。让每个测试共享相同的类实例将非常不利于测试隔离,并会促进不良的测试实践。这在下面概述
# content of test_class_demo.py
class TestClassDemoInstance:
value = 0
def test_one(self):
self.value = 1
assert self.value == 1
def test_two(self):
assert self.value == 1
$ pytest -k TestClassDemoInstance -q
.F [100%]
================================= FAILURES =================================
______________________ TestClassDemoInstance.test_two ______________________
self = <test_class_demo.TestClassDemoInstance object at 0xdeadbeef0002>
def test_two(self):
> assert self.value == 1
E assert 0 == 1
E + where 0 = <test_class_demo.TestClassDemoInstance object at 0xdeadbeef0002>.value
test_class_demo.py:9: AssertionError
========================= short test summary info ==========================
FAILED test_class_demo.py::TestClassDemoInstance::test_two - assert 0 == 1
1 failed, 1 passed in 0.12s
请注意,在类级别添加的属性是 类属性,因此它们将在测试之间共享。
使用 pytest.approx 比较浮点值¶
pytest 还提供了一些实用程序来简化测试编写。例如,你可以使用 pytest.approx() 来比较可能存在少量舍入误差的浮点值
# content of test_approx.py
import pytest
def test_sum():
assert (0.1 + 0.2) == pytest.approx(0.3)
这避免了手动容差检查或使用 math.isclose,并且适用于标量、列表和 NumPy 数组。
为功能测试请求唯一的临时目录¶
pytest 提供 内置夹具/函数参数 来请求任意资源,例如唯一的临时目录
# content of test_tmp_path.py
def test_needsfiles(tmp_path):
print(tmp_path)
assert 0
在测试函数签名中列出名称 tmp_path,pytest 将在执行测试函数调用之前查找并调用夹具工厂来创建资源。在测试运行之前,pytest 会创建一个每个测试调用唯一的临时目录
$ pytest -q test_tmp_path.py
F [100%]
================================= FAILURES =================================
_____________________________ test_needsfiles ______________________________
tmp_path = PosixPath('PYTEST_TMPDIR/test_needsfiles0')
def test_needsfiles(tmp_path):
print(tmp_path)
> assert 0
E assert 0
test_tmp_path.py:3: AssertionError
--------------------------- Captured stdout call ---------------------------
PYTEST_TMPDIR/test_needsfiles0
========================= short test summary info ==========================
FAILED test_tmp_path.py::test_needsfiles - assert 0
1 failed in 0.12s
有关临时目录处理的更多信息,请参阅 临时目录和文件。
使用该命令查找存在哪些内置 pytest 夹具
pytest --fixtures # shows builtin and custom fixtures
请注意,此命令会省略以 _ 开头的夹具,除非添加 -v 选项。
继续阅读¶
查看其他 pytest 资源,帮助你为独特的工作流程定制测试
“如何调用 pytest”获取命令行调用示例
“如何将 pytest 与现有测试套件一起使用”用于处理预先存在的测试
“如何使用属性标记测试函数”获取有关
pytest.mark机制的信息“夹具参考”为你的测试提供功能基线
“编写插件”用于管理和编写插件
“良好集成实践”用于 virtualenv 和测试布局