【Pytest】基础到高级功能的理解使用
文章目录
- 第一部分:Pytest 简介
- 1.1 什么是 Pytest?
- 1.2 Pytest 的历史
- 1.3 Pytest 的核心概念
- 1.4 Pytest 的特点
- 1.5 为什么选择 Pytest?
- 第二部分:Pytest 的基本使用
- 2.1 安装 Pytest
- 2.2 编写第一个测试用例
- 2.2.1 创建一个简单的测试函数
- 2.2.2 运行测试
- 2.3 测试报告和输出
- 2.3.1 简单输出
- 2.3.2 显示详细的失败信息
- 2.3.3 生成 HTML 报告
- 2.4 组织和运行多个测试
- 2.4.1 测试文件和测试函数命名约定
- 2.4.2 运行特定的测试用例
- 2.5 断言与调试
- 2.5.1 常见断言示例
- 2.5.2 调试断言失败
- 2.6 夹具(Fixtures)介绍
- 2.6.1 创建一个夹具
- 2.6.2 夹具的作用范围
- 第三部分:Pytest的高级功能
- 3.1 参数化测试(Parametrized Tests)
- 3.1.1 使用 @pytest.mark.parametrize 进行测试
- 3.1.2 参数化测试的使用场景
- 3.2 模拟与测试(Mocking)
- 3.2.1 使用 unittest.mock 模块
- 3.2.2 使用 pytest-mock 插件
- 3.3 测试配置(Configuration)
- 3.3.1 配置文件 pytest.ini
- 3.3.2 使用命令行选项
- 3.4 并行测试(Parallel Testing)
- 3.4.1 使用 pytest-xdist 插件
- 3.4.2 并行测试的配置与使用
- 第四部分:Pytest 插件
- 4.1 自定义插件
- 4.1.1 创建自己的 pytest 插件
- 插件创建步骤:
- 示例:创建一个简单的插件,添加一个命令行选项
- 4.1.2 插件的安装与使用
- 4.2 常用插件介绍
- 4.2.1 pytest-cov:代码覆盖率报告
- 4.2.2 pytest-html:生成 HTML 格式的测试报告
- 4.2.3 pytest-flake8:集成 flake8 代码检查
- 第五部分:Pytest 与持续集成(CI)
- 5.1 Jenkins 集成
- 5.1.1 使用 Jenkins 运行 pytest 测试
- 5.1.2 Jenkins 中配置 pytest 插件
- 5.2 GitHub Actions 集成
- 5.2.1 在 GitHub Actions 中设置 pytest 测试
- 5.2.2 自动化测试报告
- 第六部分:Pytest 常见问题解答
- 6.1 如何处理测试超时
- 6.1.1 使用 pytest-timeout 插件
- 6.2 如何跳过测试
- 6.2.1 使用 @pytest.mark.skip 和 @pytest.mark.skipif
- 6.3 如何收集测试数据
- 6.3.1 使用 pytest-cov 生成测试覆盖率报告
- 附件
- pytest 命令行参数
第一部分:Pytest 简介
1.1 什么是 Pytest?
pytest
是一个功能强大的 Python 测试框架,专为编写简单、可扩展的测试而设计。它是 Python 中最受欢迎的测试工具之一,广泛应用于单元测试、集成测试以及功能测试等领域。pytest
提供了清晰易懂的语法,并且可以轻松与其他测试框架(如 unittest
)兼容使用。
1.2 Pytest 的历史
pytest
由 Holger Krekel 于 2004 年首次发布,它的起源可以追溯到其前身 py.test
。pytest
在最初发布时就力图通过简化测试代码,使其易于使用,并且随着时间的推移,逐渐添加了大量功能和优化。
- 2004:
py.test
项目发布,提供了基础的测试框架。 - 2010:项目更名为
pytest
,并开始引入插件支持。 - 2015:
pytest
开始成为 Python 测试领域的领先工具,并逐渐广泛应用于工业界和开源项目中。
通过不断更新和增加特性,pytest
逐步成为 Python 最常用的测试框架之一,并且被许多开发者和团队广泛采用。
1.3 Pytest 的核心概念
在使用 pytest
进行测试时,我们需要理解几个核心概念:
1. 测试函数与测试类
- 测试函数:
pytest
会自动识别以test_
开头的函数,并将它们视为测试用例。我们可以直接使用 Python 内建的assert
语句来验证某些条件。 - 测试类:可以通过在类中定义以
test_
开头的方法来组织测试用例。类本身可以没有初始化方法或有必要的设置和清理工作。
2. 断言(Assertions)
断言是 pytest
测试框架的核心。我们通过 assert
语句来验证一个表达式是否成立。例如:
def test_addition():
result = 2 + 3
assert result == 5 # 如果结果不等于5,测试会失败
在 pytest
中,断言语法非常简单直接,并且可以非常清晰地显示出失败的原因,便于调试。
3. 夹具(Fixtures)
pytest
中的夹具(Fixtures)是用来准备和清理测试所需资源的组件。它可以自动处理一些资源的初始化和销毁,比如数据库连接、文件操作等。夹具可以在多个测试函数之间共享,从而减少代码的重复。
4. 参数化(Parametrization)
通过 参数化测试,可以用不同的输入数据执行同一测试方法。pytest
支持通过 @pytest.mark.parametrize
装饰器将参数化的测试逻辑应用到函数中,这在处理需要多次执行的相似测试时非常有用。
1.4 Pytest 的特点
pytest
拥有许多使其成为开发者首选测试框架的特性:
简单易用
pytest
的最大特点之一是它的简洁性。通过仅使用 Python 内建的 assert
语法,可以编写简单的测试用例。同时,它不需要复杂的配置和设置,可以立即开始编写和执行测试。
自动化测试发现
pytest
会自动发现符合命名规则的测试文件和测试函数。默认情况下,pytest
会查找以 test_
开头的函数,并执行其中的代码。
- 测试文件:以
test_
开头,或者以_test.py
结尾的 Python 文件。 - 测试函数:以
test_
开头的函数。
例如,pytest
会自动发现并执行以下文件中的测试:
test_example.py
test_calculator.py
丰富的插件支持
pytest
有一个强大的插件系统,可以扩展其功能。例如,pytest-html
可以生成 HTML 格式的测试报告,pytest-xdist
可以并行运行测试用例,pytest-cov
可以进行代码覆盖率报告等。
高度可扩展
pytest
支持通过 夹具(Fixtures)、标记(Marks)、插件(Plugins) 等方式扩展框架功能。它能够处理复杂的测试场景,满足各种不同的需求。
支持并行测试
pytest
可以通过插件支持并行执行测试用例,从而显著提高大规模测试的效率。例如,使用 pytest-xdist
插件,可以将测试分发到多个 CPU 核心进行并行执行。
与其他测试框架兼容
pytest
可以兼容 unittest
测试框架,即可以在使用 pytest
的同时继续使用旧有的 unittest
测试用例。pytest
还提供了适配器,以便将其他测试框架(如 nose
)的测试迁移到 pytest
中。
高质量的测试报告
pytest
默认会以简单的文本格式显示测试结果,但它还可以生成更详细的报告,包括失败的详细信息、调试提示等。如果我们希望生成 HTML 格式的报告,也可以使用插件来实现。
1.5 为什么选择 Pytest?
以下是一些开发者选择 pytest
的常见原因:
- 简洁和易学:由于
pytest
使用标准的 Python 断言语法,学习曲线非常平缓。开发者可以快速上手并编写有效的测试。 - 强大的功能:即使是最复杂的测试需求,
pytest
也能够处理。通过插件、夹具等特性,pytest
支持多种高级用法。 - 良好的社区支持:
pytest
拥有一个活跃的社区,几乎任何问题都有现成的解决方案或资源。 - 提高开发效率:通过自动化测试和集成工具,
pytest
能够帮助开发者更快地定位问题,并提高软件开发效率。
第二部分:Pytest 的基本使用
2.1 安装 Pytest
在开始使用 pytest
之前,首先需要安装它。可以通过 Python 的包管理工具 pip
来安装:
pip install pytest
安装完成后,可以通过以下命令验证 pytest
是否安装成功:
pytest --version
如果安装成功,会显示 pytest
的版本信息。
2.2 编写第一个测试用例
安装好 pytest
,我们就可以开始编写测试用例。pytest
会自动识别所有以 test_
开头的函数或文件,并将其视为测试用例。
2.2.1 创建一个简单的测试函数
首先,在一个新的 Python 文件中(例如 test_sample.py
),编写一个简单的测试函数:
# test_sample.py
def test_addition():
assert 2 + 3 == 5
def test_subtraction():
assert 114514 - 1919 == 112595
在这个例子中,我们创建了两个测试函数 test_addition
和 test_subtraction
,并在其中使用了 assert
语句来验证基本的数学运算。
2.2.2 运行测试
通过命令行运行 pytest
,它会自动识别并执行以 test_
开头的测试函数。
pytest test_sample.py
运行后,pytest
会列出所有测试的结果。例如:
=========================== test session starts ============================
collected 2 items
test_sample.py .. [100%]
============================ 2 passed in 0.03 seconds =====================
这里的 ..
表示两个测试都通过了。测试通过后,pytest
会输出相关的通过信息,并且可以选择性地生成更详细的报告。
2.3 测试报告和输出
默认情况下,pytest
输出测试结果到控制台,包括测试是否通过、失败以及跳过的测试等。我们还可以使用以下选项来修改输出的详细程度:
2.3.1 简单输出
运行 pytest
时,添加 -q
参数可以减少输出的详细程度,输出更为简洁:
pytest -q test_sample.py
这将只显示测试的摘要信息,如下所示:
.. [100%]
2.3.2 显示详细的失败信息
当测试失败时,pytest
会自动显示失败的详细信息。例如,如果在 test_addition
中断言失败,pytest
会给出详细的错误信息和调试提示,帮助开发者快速定位问题。
> assert 2 + 3 == 6
E assert 5 == 6
2.3.3 生成 HTML 报告
还可以通过 pytest
插件生成 HTML 格式的报告。首先,安装 pytest-html
插件:
pip install pytest-html
然后,运行 pytest
并指定生成报告的输出文件:
pytest --html=report.html
这样,测试结果将保存在 report.html
文件中,并可以通过浏览器查看详细的测试报告。
2.4 组织和运行多个测试
2.4.1 测试文件和测试函数命名约定
pytest
会自动发现符合一定命名规则的测试用例。默认的命名规则是:
- 测试文件:以
test_
开头,或者以_test
结尾的 Python 文件。 - 测试函数:以
test_
开头的函数。
例如,以下文件和函数会被 pytest
自动发现并运行:
- 文件:
test_example.py
,example_test.py
- 函数:
test_addition()
,test_subtraction()
2.4.2 运行特定的测试用例
通过文件名或函数名可以指定运行特定的测试,类似cpp命名空间的用法。例如,运行 test_addition
函数:
pytest test_sample.py::test_addition
这样,pytest
只会执行 test_addition
函数,而不会执行文件中的其他测试函数。
2.5 断言与调试
pytest
默认使用 Python 内建的 assert
语句来进行断言,这使得测试代码非常简洁。通过断言,pytest
可以验证期望的结果是否与实际结果一致。
2.5.1 常见断言示例
-
比较数字:
def test_addition(): assert 2 + 3 == 5
-
比较字符串:
def test_string(): assert "Hello" == "Hello"
-
检查是否抛出异常:
pytest
允许检查某个操作是否会抛出异常。例如,验证除以零时是否抛出ZeroDivisionError
:import pytest def test_zero_division(): with pytest.raises(ZeroDivisionError): 1 / 0
2.5.2 调试断言失败
当 assert
语句失败时,pytest
会显示失败的断言表达式,并给出失败的提示。为了调试失败的测试,还可以在运行时使用 pytest
的 -s
选项,这样就可以在失败的地方查看输出或交互式调试。
pytest -s test_sample.py
2.6 夹具(Fixtures)介绍
pytest
的夹具是用来在测试运行之前或之后执行某些准备工作和清理工作的。通过夹具,可以实现数据库连接、文件创建等操作,并在多个测试之间共享这些资源。
2.6.1 创建一个夹具
夹具通过 @pytest.fixture
装饰器创建。以下是一个简单的夹具示例:
import pytest
@pytest.fixture
def setup_data():
data = {"name": "Alice", "age": 30}
return data
def test_name(setup_data):
assert setup_data["name"] == "Alice"
def test_age(setup_data):
assert setup_data["age"] == 30
在上面的示例中,setup_data
是一个夹具,它会在每个测试函数运行之前自动提供数据。
2.6.2 夹具的作用范围
夹具的作用范围(scope)可以通过 scope
参数来设置,常见的范围有:
function
:每个测试函数都会创建新的夹具实例(默认)。module
:每个模块只会创建一次夹具实例。session
:在整个测试会话期间只会创建一个夹具实例。
例如,创建一个模块级别的夹具:
@pytest.fixture(scope="module")
def setup_database():
# 设置数据库连接
db = create_db_connection()
yield db # 测试执行完毕后关闭数据库连接
db.close()
第三部分:Pytest的高级功能
3.1 参数化测试(Parametrized Tests)
3.1.1 使用 @pytest.mark.parametrize 进行测试
@pytest.mark.parametrize
是 Pytest 提供的一个装饰器,用于实现参数化测试。通过它,我们可以对同一个测试用例提供多个不同的输入数据,这样每次运行测试时都会用不同的参数执行,避免重复编写多个测试函数。
import pytest
# 参数化测试,test_addition 会分别用 (1, 2, 3) 和 (4, 5, 9) 这两个数据集来执行
@pytest.mark.parametrize("a, b, expected", [(1, 2, 3), (4, 5, 9), (7, 8, 15)])
def test_addition(a, b, expected):
assert a + b == expected
上面的代码中,@pytest.mark.parametrize
使得 test_addition
测试函数能够接受三个参数 a
, b
和 expected
,并且每个参数集分别对应 (1, 2, 3)
、(4, 5, 9)
和 (7, 8, 15)
。运行时,Pytest 会为每组输入值自动执行该测试。
3.1.2 参数化测试的使用场景
参数化测试通常适用于以下几种场景:
-
多组输入输出验证:当我们需要验证函数或方法的多个输入和输出时,可以使用参数化来减少代码重复。例如,测试一个加法函数对于多个输入的行为。
-
多种边界条件的测试:测试时需要覆盖多个边界条件(如最大值、最小值、负值等),使用参数化可以很容易地针对每个边界值进行验证。
-
提升测试效率:当测试数据量大时,参数化可以将相似的测试代码合并成一个测试用例,提高测试的效率与可读性。
3.2 模拟与测试(Mocking)
3.2.1 使用 unittest.mock 模块
在进行单元测试时,我们可能会遇到外部依赖的情况(例如数据库查询、API 请求等),为了避免这些外部依赖影响测试的独立性,我们通常需要模拟这些外部依赖。
Python 提供了 unittest.mock
模块来创建模拟对象,并替代真正的外部依赖。下面是一个使用 unittest.mock
模块的示例:
from unittest.mock import MagicMock
import requests
def fetch_data(url):
response = requests.get(url)
return response.json()
def test_fetch_data():
mock_response = MagicMock()
mock_response.json.return_value = {"key": "value"}
# 模拟 requests.get 返回模拟的响应对象
with patch('requests.get', return_value=mock_response):
result = fetch_data('http://example.com')
assert result == {"key": "value"}
在上面的代码中,patch
用来模拟 requests.get
方法,使得 fetch_data
方法使用的是模拟的响应对象,而不是实际进行网络请求。
3.2.2 使用 pytest-mock 插件
pytest-mock
是一个为 Pytest 提供更友好接口的插件,它基于 unittest.mock
,但更加简化了模拟操作。使用 pytest-mock
,可以直接在测试函数中通过 mocker
fixture 来进行模拟。
import requests
def fetch_data(url):
response = requests.get(url)
return response.json()
def test_fetch_data(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {"key": "value"}
# 使用 mocker 模拟 requests.get
mocker.patch('requests.get', return_value=mock_response)
result = fetch_data('http://example.com')
assert result == {"key": "value"}
在这个示例中,mocker.patch
替代了 requests.get
方法并返回模拟的响应对象,整个过程更加简洁和易读。
3.3 测试配置(Configuration)
3.3.1 配置文件 pytest.ini
pytest.ini
是一个配置文件,允许我们在项目根目录下定义一些 Pytest 的全局配置选项,比如测试时使用的标记、日志级别、插件等。一个常见的 pytest.ini
配置文件如下:
[pytest]
# 定义自定义标记
markers =
slow: 标记为慢速测试
smoke: 标记为冒烟测试
# 设置默认的日志级别
log_cli_level = INFO
# 配置插件
addopts = --maxfail=1 --disable-warnings
在上面的示例中,我们定义了两种标记 slow
和 smoke
,同时设置了日志级别和命令行选项。
3.3.2 使用命令行选项
Pytest 提供了多个命令行选项,方便我们在运行测试时指定一些配置参数。下面列举几个常用的命令行选项:
-v
:显示详细的测试输出。-k
:根据表达式过滤测试函数名。--maxfail
:指定最大失败次数,超过该次数后停止运行。--disable-warnings
:禁用警告信息。
例如:
pytest -v --maxfail=2 --disable-warnings
此命令会运行所有测试,并且如果有超过两次失败就停止,且不显示警告信息。
详细的pytest命令行选项
3.4 并行测试(Parallel Testing)
3.4.1 使用 pytest-xdist 插件
pytest-xdist
插件允许我们在多个 CPU 核心上并行运行测试,从而提高测试效率。通过安装并配置 pytest-xdist
,我们可以使用命令行选项 -n
来指定并行测试的数量。
首先,安装插件:
pip install pytest-xdist
然后,使用以下命令来运行并行测试:
pytest -n 4
这条命令会在 4 个并行进程中执行测试,从而提高运行速度。
3.4.2 并行测试的配置与使用
在并行测试时,我们可以通过 pytest-xdist
来进一步配置,如控制测试的并发数、并行运行时避免共享状态等。
例如,运行时,如果需要隔离测试的状态,可以使用 --dist
和 --tx
选项来进行更多控制:
pytest -n 4 --dist=loadscope --tx ssh=remotehost//python=python3
这个命令表示使用 4 个进程来运行测试,并将每个进程的负载均匀分配到每个核心。通过 --tx
选项,可以将测试分发到远程主机上进行并行运行。
第四部分:Pytest 插件
4.1 自定义插件
4.1.1 创建自己的 pytest 插件
创建 Pytest 插件可以帮助扩展 Pytest 的功能,例如自定义命令行选项、配置、测试钩子等。Pytest 插件一般由两个部分组成:一个是定义插件行为的 Python 代码,另一个是一个配置文件(通常是 pytest.ini
或其他配置文件)。
插件创建步骤:
- 创建一个 Python 文件:在项目根目录或
conftest.py
中编写插件代码。 - 实现钩子函数:Pytest 插件通过钩子函数(hook functions)来定制功能。常见的钩子函数包括
pytest_runtest_protocol
、pytest_configure
等。 - 注册插件:插件通过
pytest_plugins
在conftest.py
或pytest.ini
中进行注册。
示例:创建一个简单的插件,添加一个命令行选项
- 创建一个 Python 文件
myplugin.py
,并实现一个命令行选项:
# myplugin.py
import pytest
def pytest_addoption(parser):
parser.addoption(
"--myoption", action="store", default="default_value", help="Custom option"
)
@pytest.fixture
def myoption(request):
return request.config.getoption("--myoption")
- 在测试文件中使用这个选项:
def test_with_option(myoption):
assert myoption == "default_value"
- 使用
pytest.ini
来注册插件:
# pytest.ini
[pytest]
plugins =
myplugin
通过上面的步骤,创建了一个简单的插件,它为 Pytest 添加了一个名为 --myoption
的命令行选项。
4.1.2 插件的安装与使用
自定义插件可以通过 Pytest 配置文件进行注册,或者直接安装并在测试运行时启用。如果开发了一个插件并想要安装和使用它(nb),可以将插件发布到 PyPI(Python 包索引)中,然后通过 pip install
安装。以下是安装和使用插件的基本步骤:
-
安装插件:
pip install pytest-myplugin
-
启用插件:在
pytest.ini
或conftest.py
中注册插件,或在命令行通过-p
选项指定插件。pytest -p myplugin
这样,就可以在 Pytest 中使用自定义插件了。
4.2 常用插件介绍
4.2.1 pytest-cov:代码覆盖率报告
pytest-cov
插件用于生成代码覆盖率报告,帮助开发者了解代码的测试覆盖情况。它集成了 coverage.py
,并能够输出多种格式的报告,如终端输出、HTML 或 XML。
安装:
pip install pytest-cov
使用:
-
命令行选项:可以通过
--cov
选项来指定要进行覆盖率检查的模块或包。pytest --cov=my_module
这条命令会运行
my_module
的测试并生成代码覆盖率报告。 -
生成 HTML 报告:
可以通过
--cov-report
选项指定生成的报告格式。例如,生成 HTML 格式的覆盖率报告:pytest --cov=my_module --cov-report html
这会在当前目录下生成一个
htmlcov
文件夹,其中包含了 HTML 格式的代码覆盖率报告。 -
多项覆盖率报告:还可以同时输出多种格式的报告:
pytest --cov=my_module --cov-report html --cov-report term
这将同时生成 HTML 和终端格式的报告。
4.2.2 pytest-html:生成 HTML 格式的测试报告
pytest-html
插件用于生成漂亮的 HTML 测试报告,包含测试结果的详细信息,如每个测试用例的执行状态、日志、截图等。
安装:
pip install pytest-html
使用:
-
生成 HTML 测试报告:
运行测试并生成一个
report.html
文件:pytest --html=report.html
-
自定义报告标题和描述:
可以通过
--self-contained-html
选项生成一个独立的 HTML 文件,其中包含所有的资源(如 CSS 和 JavaScript),使报告文件更具可移植性:pytest --html=report.html --self-contained-html
-
自定义报告内容:
pytest-html
允许在报告中加入自定义的内容(如测试前后的钩子):# conftest.py def pytest_html_report_title(report): report.title = "Custom Test Report"
这样,生成的报告标题会被替换为 “Custom Test Report”。
4.2.3 pytest-flake8:集成 flake8 代码检查
pytest-flake8
插件集成了 flake8
工具,允许运行 Pytest 测试时自动检查代码的样式问题(如 PEP 8 规范)。如果代码违反了样式规则,Pytest 会报告出错的地方。
安装:
pip install pytest-flake8
使用:
-
运行 Flake8 检查:
在运行测试时,Pytest 会自动执行
flake8
检查:pytest --flake8
-
配置 Flake8 选项:可以通过在项目根目录下创建
.flake8
配置文件来调整 Flake8 的行为:# .flake8 [flake8] max-line-length = 120
通过这个配置文件,可以调整行长度限制或其他
flake8
的设置。
第五部分:Pytest 与持续集成(CI)
在持续集成(CI)流程中,Pytest 可与 Jenkins、GitHub Actions 等工具集成,自动化执行测试并生成报告,帮助团队确保代码质量和快速发现潜在问题。
5.1 Jenkins 集成
Jenkins 是一款广泛使用的自动化服务器,支持构建、部署、测试等各种自动化任务。通过在 Jenkins 中集成 Pytest,可以实现自动化测试,确保代码在每次更改后都经过验证。
5.1.1 使用 Jenkins 运行 pytest 测试
-
安装 Jenkins 和必要的插件
- 安装 Jenkins 服务器,并确保已经安装了必要的插件,如 Pipeline、Git 等。
- 如果要使用 Pytest 的代码覆盖率插件,可以安装 Coverage 插件。
-
设置 Jenkins 任务
可以通过创建一个新任务或配置现有任务来运行 Pytest 测试。假设已经有一个 Git 仓库,并且测试代码存在于该仓库中。
- 在 Jenkins 中,点击 新建任务,然后选择 自由风格项目。
- 在 构建触发器 部分,可以选择例如 GitHub webhook,以便每次代码更改时自动触发构建。
-
配置构建步骤
在 构建 部分,可以添加一个执行 Shell 或批处理命令的步骤来运行 Pytest。
如果要使用虚拟环境,可以通过以下命令在构建中运行 Pytest:
# 激活虚拟环境 source venv/bin/activate # 或者其他合适的命令 # 安装依赖 pip install -r requirements.txt # 运行 pytest pytest --maxfail=1 --disable-warnings -q
还可以根据需要修改
pytest
命令,如生成 HTML 或 JUnit 格式的报告。 -
运行构建
保存并启动构建,Jenkins 将会自动执行测试,并在控制台输出测试结果。
5.1.2 Jenkins 中配置 pytest 插件
-
安装 Pytest 插件
Jenkins 提供了一些插件,可以与 Pytest 集成,特别是 JUnit 插件,它可以解析 Pytest 生成的 JUnit 格式的测试结果,并在 Jenkins 中展示。可以通过以下步骤来配置:
-
在 Jenkins 上的任务配置界面,选择 构建后操作。
-
添加 发布JUnit测试报告 步骤。
-
在测试报告 XML 文件路径中,填入 Pytest 生成的 JUnit 格式报告路径。例如:
pytest --junitxml=report.xml
这将生成一个
report.xml
文件,其中包含测试的详细信息,Jenkins 可以解析这个文件并在界面上展示测试结果。
-
-
配置 HTML 报告
如果希望生成更美观的 HTML 测试报告,可以使用
pytest-html
插件,并将生成的 HTML 文件发布到 Jenkins:-
使用以下命令生成 HTML 格式的报告:
pytest --html=report.html
-
然后,可以将 HTML 文件通过 Jenkins 任务的 构建后操作 上传并作为构建的输出文件查看。
-
5.2 GitHub Actions 集成
GitHub Actions 是 GitHub 提供的一种自动化 CI/CD 服务,能够在代码提交时自动运行测试、部署应用等。它能够无缝集成 Pytest,以确保每次提交都经过测试验证。
5.2.1 在 GitHub Actions 中设置 pytest 测试
-
创建 GitHub Actions 配置文件
GitHub Actions 的配置文件是 YAML 格式,通常存放在项目中的
.github/workflows
文件夹下。创建一个新的文件pytest.yml
,并添加以下内容:name: Run Pytest on: push: branches: - main pull_request: branches: - main jobs: test: runs-on: ubuntu-latest steps: - name: Check out the repository uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.8' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt - name: Run Pytest run: | pytest --maxfail=1 --disable-warnings -q
这个配置会在代码推送或拉取请求时,自动在 GitHub Actions 上运行 Pytest 测试。
-
解析测试结果
如果希望 GitHub Actions 在每次运行后报告测试结果,可以配置 Pytest 生成 JUnit 格式的报告并使用 GitHub Actions 的 JUnit 测试结果解析器:
在
pytest.yml
中添加以下步骤:- name: Run Pytest and generate JUnit report run: | pytest --junitxml=test_results.xml - name: Upload JUnit test results uses: actions/upload-artifact@v2 with: name: pytest-results path: test_results.xml
这样,测试结果将被上传为构建工件,可以在 GitHub Actions 界面上查看。
5.2.2 自动化测试报告
-
生成并上传 HTML 报告
如果希望生成更详细和美观的 HTML 格式测试报告,可以使用
pytest-html
插件,并将报告作为构建工件上传:- name: Install pytest-html run: pip install pytest-html - name: Run Pytest with HTML report run: pytest --html=report.html - name: Upload HTML report uses: actions/upload-artifact@v2 with: name: pytest-html-report path: report.html
这样,每次运行测试后都能获取一个详细的 HTML 测试报告,并且可以通过 GitHub Actions 界面下载和查看该报告。
-
可视化测试结果
如果想将 Pytest 结果直接以图形化的方式显示在 GitHub Actions 界面上,可以使用第三方工具或插件(如 pytest-azure-pipelines)来集成更丰富的报告和通知功能,提供更加可视化的测试反馈。
通过将 Pytest 集成到持续集成系统中(如 Jenkins 或 GitHub Actions),可以在每次代码变更后自动执行测试,并生成详细的报告,从而确保软件质量。自动化测试能够帮助团队快速发现并修复问题,从而提高开发效率。
第六部分:Pytest 常见问题解答
在使用 Pytest 进行自动化测试时,开发人员可能会遇到一些常见问题,如测试超时、跳过某些测试和收集测试覆盖率等。以下是解决这些问题的一些方法。
6.1 如何处理测试超时
有时候,某些测试可能会因某些原因超时(如依赖外部服务或执行较复杂的操作)。为了解决这个问题,可以使用 pytest-timeout
插件来设置测试的超时时间。
6.1.1 使用 pytest-timeout 插件
pytest-timeout
插件可以为每个测试设置超时时间,并在超时后强制终止该测试。
-
安装插件
要使用
pytest-timeout
插件,首先需要通过 pip 安装:pip install pytest-timeout
-
配置超时设置
可以在命令行中设置超时,也可以使用装饰器在测试函数中配置超时。
-
在命令行中设置超时:
可以通过
--timeout
参数为所有测试设置超时。例如,设置每个测试的超时时间为 10 秒:pytest --timeout=10
-
使用装饰器设置超时:
还可以使用
@pytest.mark.timeout
装饰器为单个测试或测试类设置超时时间。例如,设置某个特定测试的超时时间为 5 秒:import pytest @pytest.mark.timeout(5) def test_example(): # 测试逻辑 assert True
-
-
处理超时异常
默认情况下,当超时发生时,
pytest-timeout
会抛出一个TimeoutError
,并将该测试标记为失败。可以通过--timeout-method
参数设置超时处理方式:exit
: 在超时后立刻终止测试。signal
: 使用操作系统信号终止超时测试。thread
: 通过线程来中止测试。
示例:
pytest --timeout=10 --timeout-method=exit
6.2 如何跳过测试
在某些情况下,可能希望跳过某些测试,或者根据条件决定是否执行某些测试。Pytest 提供了两种常见的跳过方法:@pytest.mark.skip
和 @pytest.mark.skipif
。
6.2.1 使用 @pytest.mark.skip 和 @pytest.mark.skipif
-
使用
@pytest.mark.skip
跳过测试如果希望强制跳过某个测试,可以使用
@pytest.mark.skip
装饰器。被装饰的测试将始终跳过,不会被执行。import pytest @pytest.mark.skip(reason="暂时跳过这个测试") def test_example(): assert 1 == 1
在上面的例子中,
test_example
测试会被跳过,并且在测试报告中显示跳过的原因。 -
使用
@pytest.mark.skipif
根据条件跳过测试如果只希望在特定条件下跳过测试,可以使用
@pytest.mark.skipif
装饰器。该装饰器接受一个条件表达式,只有当该条件为真时,测试才会被跳过。例如,假设希望仅在 Python 版本低于 3.7 时跳过某个测试:
import pytest @pytest.mark.skipif( pytest.__version__ < "3.7", reason="Python 版本低于 3.7,跳过测试" ) def test_example(): assert 1 == 1
在这个例子中,如果 pytest 版本低于 3.7,
test_example
测试将被跳过。
6.3 如何收集测试数据
测试数据的收集对确保代码覆盖率以及了解测试的执行情况至关重要。Pytest 提供了多种方法来收集和分析测试数据。
6.3.1 使用 pytest-cov 生成测试覆盖率报告
pytest-cov
插件用于收集代码覆盖率数据,并生成相应的报告。它允许查看测试执行期间代码的覆盖情况,帮助我们发现未被测试到的代码部分。
-
安装 pytest-cov 插件
首先,需要安装
pytest-cov
插件:pip install pytest-cov
-
运行测试并生成覆盖率报告
运行 Pytest 时,可以通过
--cov
参数指定希望收集覆盖率的代码文件或模块。例如,收集my_module
模块的代码覆盖率:pytest --cov=my_module
该命令会执行测试,并在控制台输出覆盖率报告。
-
生成详细的覆盖率报告
如果希望将覆盖率报告输出为 HTML 格式,可以使用
--cov-report
参数:pytest --cov=my_module --cov-report=html
这将生成一个
htmlcov
目录,其中包含一个详细的 HTML 格式的覆盖率报告。 -
生成其他格式的报告
除了 HTML 格式,还可以生成其他格式的报告(如
xml
或term
),以便于进一步的分析或与其他工具集成:-
生成 XML 格式的报告:
pytest --cov=my_module --cov-report=xml
-
在终端中输出覆盖率统计信息:
pytest --cov=my_module --cov-report=term
-
-
配置覆盖率的阈值
可以配置覆盖率阈值,确保覆盖率达到预期。例如,设置最低的覆盖率为 80%:
pytest --cov=my_module --cov-fail-under=80
如果覆盖率低于 80%,测试将失败。
附件
pytest 命令行参数
参数 | 描述 |
---|---|
-q 或 --quiet | 简化输出,显示最简洁的测试结果。 |
-v 或 --verbose | 显示详细输出,包括每个测试用例的名称和状态。 |
-k | 选择性运行匹配特定表达式的测试,如 -k "test_name" 。 |
-x 或 --exitfirst | 第一个测试失败时立即停止执行。 |
--maxfail=num | 设置最大失败次数,超过后停止执行,如 --maxfail=3 。 |
-s | 显示标准输出和错误输出。 |
--disable-warnings | 禁用警告信息的显示。 |
--tb=short 或 --tb=long | 设置错误回溯信息的显示方式,short 显示简洁回溯,long 显示详细回溯。 |
--html=report.html | 生成 HTML 格式的测试报告,保存为 report.html 。 |
--maxfail=NUM | 设置最大失败数,达到时停止测试。 |
--capture=no | 禁止捕获测试输出,显示实时输出。 |
--runxfail | 即使标记为“预期失败”的测试失败,也将其视为成功。 |
--tb=auto | 根据测试结果自动选择回溯信息的展示方式。 |
--junitxml=path | 输出 JUnit 风格的 XML 测试报告到指定路径。 |
--pdb | 测试失败时启用 Python 调试器 (pdb),可以进行交互式调试。 |