【python】之pytest使用指南快速入门(一)

一 介绍

The pytest framework makes it easy to write small, readable tests, and can scale to support complex functional testing for applications and libraries.

pytest框架可以轻松编写小型、可读的测试,并且可以扩展以支持应用程序和库的复杂功能测试。

结构简单、可伸缩、容易使用、测试具有表达性和可读性,

不需要样板代码,快速开始对应用程序或库进行小的单元测试或复杂的功能测试

1 安装和入门

1.1 安装

  1. 在命令行中运行以下命令:
1
pip install -U pytest
  1. 检查是否安装了正确的版本:
1
2
$ pytest --version
pytest 6.2.1

1.2 创建第一个测试

用四行代码创建一个简单的测试函数:

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
20
21
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.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: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

1.2 运行多个测试

pytest will run all files of the form test_*.py or *_test.py in the current directory and its subdirectories. More generally, it follows standard test discovery rules.

pytest 将运行当前目录及其子目录中 test_*.py 或 *_test.py 形式的所有文件。更一般地说,它遵循标准测试发现规则

Python测试发现的约定

Pytest实现了以下标准测试发现:

  • 如果没有指定参数,则从testpaths(如果配置了)或当前目录开始收集。或者,命令行参数可以在目录、文件名或节点id的任意组合中使用。

  • 递归到目录中,除非它们匹配norecursedirs。

  • 在这些目录中,搜索test_*.py或*_test.py文件,通过它们的测试包名导入。

  • 从这些文件中,收集测试项目:

    • 在类之外添加Test前缀的测试函数或方法。

    • test前缀测试函数或方法在test前缀测试类中(不带__init__方法)。还考虑了用@staticmethod和@classmethods装饰的方法。

例如,如何自定义您的测试发现更改标准(Python)测试发现。

在Python模块中,pytest还使用标准unittest发现测试。TestCase子类化技术。

1.4 断言引发了某种异常

使用 raises 帮助器来断言某些代码引发异常:

1
2
3
4
5
6
7
8
9
# content of test_sysexit.py
import pytest

def f():
raise SystemExit(1)

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

还可以使用 raise 提供的上下文来断言预期异常是引发的 ExceptionGroup 的一部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# content of test_exceptiongroup.py
import pytest


def f():
raise ExceptionGroup(
"Group message",
[
RuntimeError(),
],
)


def test_exception_in_group():
with pytest.raises(ExceptionGroup) as excinfo:
f()
assert excinfo.group_contains(RuntimeError)
assert not excinfo.group_contains(TypeError)

Execute the test function with “quiet” reporting mode:

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

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

1.5 将多个测试分组(封装)到一个类中

定义了多个测试(def test_xxx() ),可能希望将它们分组到一个类中。 pytest 可以轻松创建包含多个测试的类:

1
2
3
4
5
6
7
8
9
# 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前缀,否则将跳过该类。我们可以简单地通过传递它的文件名来运行模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ 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

The first test passed and the second failed. You can easily see the intermediate values in the assertion to help you understand the reason for the failure.

第一次测试通过了,第二次测试失败了。可以轻松查看断言中的中间值(失败处),以帮助了解失败的原因。

Grouping tests in classes can be beneficial for the following reasons:

  • Test organization
  • Sharing fixtures for tests only in that particular class
  • Applying marks at the class level and having them implicitly apply to all tests

将测试分组到班级中可能是有益的,原因如下:

  • 测试组织(这句话的意思是分类)
  • 仅在该特定类中共享测试装置(单独应用fixture)
  • 在类级别标记(pytest.mark)并将其隐式应用于所有测试

Something to be aware of when grouping tests inside classes is that each test has a unique instance of the class. Having each test share the same class instance would be very detrimental to test isolation and would promote poor test practices. This is outlined below:

在类中对测试进行分组时需要注意的是,每个测试都有一个唯一的类实例。让每个测试共享相同的类实例将非常不利于测试隔离,并会促进糟糕的测试实践。这是概述如下:

(测试隔离:例如测试A类和B类都有自己的属性(或变量),避免两个测试类的属性串了,Tips :尽量要写全局变量 )

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
# 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

Note that attributes added at class level are class attributes, so they will be shared between tests.

请注意,在类级别添加的属性是类属性,因此它们将在测试用例之间共享。

测试用例:def test_xxx():

1.6 请求一个用于功能测试的唯一临时目录

Request a unique temporary directory for functional tests

pytest provides Builtin fixtures/function arguments to request arbitrary resources, like a unique temporary directory:

pytest 提供内置固定装置/函数参数来请求任意资源,例如唯一的临时目录:

内置固定装置: 就是fixtures

1
2
3
4
# content of test_tmp_path.py
def test_needsfiles(tmp_path): # 这里的tmp_path 就是fixtures
print(tmp_path)
assert 0

List the name tmp_path in the test function signature and pytest will lookup and call a fixture factory to create the resource before performing the test function call. Before the test runs, pytest creates a unique-per-test-invocation temporary directory:

在测试函数签名中列出名称tmp_path, pytest将在执行测试函数调用之前查找并调用一个fixture工厂来创建资源。在测试运行之前,pytest创建一个每个测试调用唯一的临时目录:

在测试函数加入参数tmp_path,

pytest将在执行测试函数之前调用fixture里面的tmp_path方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ 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

More info on temporary directory handling is available at Temporary directories and files.

有关临时目录处理的更多信息,请参阅临时目录和文件(后面会讲哒)

Find out what kind of builtin pytest fixtures exist with the command:

使用以下命令可以找出存在哪种内置 pytest 装置:pytest –fixtures

1
pytest --fixtures   # shows builtin and custom fixtures

Note that this command omits fixtures with leading _ unless the -v option is added.

请注意,除非添加 -v 选项,否则此命令会忽略带有前导 _ 的装置。

未完待续……


【python】之pytest使用指南快速入门(一)
http://example.com/2024/01/20/661python之pytest使用指南-一/
作者
Wangxiaowang
发布于
2024年1月20日
许可协议