pytest02 Installation and Getting Started

PDF文档download latest

pytest 是一个使构建简单和可伸缩的测试变得容易的框架。测试具有表达性和可读性,不需要样板代码。几分钟后就可以开始对应用程序或库进行小的单元测试或复杂的功能测试。

安装 pytest

  1. 在命令行中运行以下命令:
1
pip install -U pytest
  1. 检查是否安装了正确的版本:
1
2
$ pytest --version
This is pytest version 4.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py

创建第一个测试

创建一个简单的测试函数:

1
2
3
4
5
6
# content of test_sample.py
def func(x):
return x + 1

def test_answer():
assert func(3) == 5

现在可以执行测试功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
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:5: AssertionError
========================= 1 failed in 0.12 seconds =========================

此测试返回失败报告,因为 func(3) 不返 5 .

运行多个测试

pytest 将在当前目录及其子目录中,运行所有这种形式的文件:test _*.py 或者 * _test.py。

断言引发了某个异常

使用 raises 断言某些代码引发异常:

1
2
3
4
5
6
7
8
# content of test_sysexit.py
import pytest
def f():
raise SystemExit(1)

def test_mytest():
with pytest.raises(SystemExit):
f()

以“ quiet ”报告模式执行测试功能:

1
2
3
$ pytest -q test_sysexit.py
. [100%]
1 passed in 0.12 seconds

将一个类中的多个测试分组

一旦开发了多个测试任务,可能希望将它们分组到一个类中。pytest很容易创建包含多个测试的类:

1
2
3
4
5
6
7
8
9
# content of test_class.py
class TestClass(object):
def test_one(self):
x = "this"
assert 'h' in x

def test_two(self):
x = "hello"
assert hasattr(x, 'check')

pytest 会发现所有带 test_前缀的函数。不需要对任何内容进行子类化。我们只需通过传递模块的文件名来运行它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ pytest -q test_class.py
.F [100%]
================================= FAILURES =================================
____________________________ TestClass.test_two ____________________________

self = <test_class.TestClass object at 0xdeadbeef>

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
1 failed, 1 passed in 0.12 seconds

第一个测试函数通过,第二个失败。容易看到断言中的中间值,理解运行失败的原因。

请求功能测试的唯一临时目录

pytest 提供了 Builtin fixtures/function arguments 请求任意资源,如唯一的临时目录:

1
2
3
# content of test_tmpdir.py
def test_needsfiles(tmpdir):
print(tmpdir)

在测试函数签名中列出 tmpdir 名字, pytest 将在执行测试函数调用之前查找并调用一个fixture工厂以创建资源。在测试运行之前, pytest 创建一个unique-per-test-invocation(唯一的每个测试调用的)临时目录:

试下了,有时候这个临时目录不会自动删除,于是写一个 fixture 来删除:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import shutil
import pytest
import json

@pytest.fixture(scope="session", autouse=True)
def cleanup(request):
# 获取基临时目录
tmp_dir = request.config._tmpdirhandler.getbasetemp()
yield tmp_dir
# 测试会话结束后删除基临时目录
shutil.rmtree(tmp_dir, ignore_errors=True)


def test_needsfiles(cleanup, tmp_path):
print(f"cleanup: {cleanup}")
print(f"tmp_path: {tmp_path}")
dict_data = {"hello": "world"}
file = tmp_path / "test.txt"
with open(file, "w") as f:
json.dump(dict_data, f)
with open(file, "r") as f:
json_data = json.load(f)
assert json_data == dict_data

"""
cleanup: C:\Users\yuping\AppData\Local\Temp\pytest-of-yuping\pytest-8
tmp_path: C:\Users\yuping\AppData\Local\Temp\pytest-of-yuping\pytest-8\test_needsfiles0
"""

找出其他的内置 pytest fixtures 使用以下命令:

1
pytest --fixtures   # shows builtin and custom fixtures

常用的内置 fixture

  1. request:
    • request 是一个非常强大的内置 fixture,它提供了对当前测试请求的访问。通过 request,你可以获取到诸如当前测试函数的名称、参数、配置选项等信息。
    • 常见的用法包括:request.node 获取当前节点(可以是测试函数或测试类),request.config 访问配置对象,request.param 在参数化测试中获取当前参数等。
  2. tmp_pathtmp_path_factory:
    • tmp_path 提供了一个临时目录的路径,该路径在测试结束时会被自动清理。这对于需要临时文件或目录的测试非常有用。
    • tmp_path_factory 可以用来创建多个临时目录,通常用于需要多个独立临时目录的场景。
  3. capfdcapsys:
    • capfdcapsys 用于捕获标准输出和标准错误流。capsys 适用于捕获 stdoutstderr 的文本输出,而 capfd 则适用于捕获文件描述符级别的输出。
    • 例如,你可以使用 capsys.readouterr() 来获取捕获的输出。
  4. monkeypatch:
    • monkeypatch 用于在测试期间动态修改模块、类或方法的行为。这在需要模拟外部依赖或环境变量时非常有用。
    • 例如,你可以使用 monkeypatch.setattr 来替换一个方法或属性,或者使用 monkeypatch.setenv 来设置环境变量。
  5. recwarn:
    • recwarn 用于捕获警告。这对于测试代码中是否正确地触发了预期的警告非常有用。
    • 例如,你可以使用 recwarn.pop(UserWarning) 来检查是否触发了特定类型的警告。
  6. pytestconfig:
    • pytestconfig 提供了对 pytest 配置对象的访问,可以用来获取命令行参数或配置文件中的选项。
    • 例如,你可以使用 pytestconfig.getoption 来获取某个配置选项的值。
  7. record_property, record_xml_property:
    • 这些 fixture 用于记录额外的测试属性,这些属性可以在测试报告中显示。
    • 例如,你可以使用 record_property("key", "value") 来记录一个键值对。
  8. usefixtures:
    • usefixtures 不是一个 fixture,而是一个装饰器,用于在测试函数或类上显式地使用一个或多个 fixture。
    • 例如,@pytest.mark.usefixtures("my_fixture") 可以用来指定某个测试函数或类使用 my_fixture
  9. filterwarnings:
    • filterwarnings 用于过滤掉特定的警告,避免它们干扰测试输出。
    • 例如,你可以使用 @pytest.mark.filterwarnings("ignore:.*DeprecationWarning") 来忽略所有 DeprecationWarning