详解在Pytest中忽略测试目录的三种方法
关注开源优测不迷路
大数据测试过程、策略及挑战
测试框架原理,构建成功的基石
在自动化测试工作之前,你应该知道的10条建议
在自动化测试中,重要的不是工具
你是否曾因无关或过时的代码导致测试失败?
这可能会增加调试和故障排除的难度,还会造成不必要的测试执行时间。或者你有正在开发中的代码,还不适合进行测试。
在这些情况下,你可能想从测试套件中排除某些测试。但在 Pytest 中该怎么做呢?你当然可以在相关目录中运行 pytest,但这效率并不高。
好消息是,忽略测试目录完全可行,而且非常简单!
借助 Pytest,通过命令行选项、标记和配置文件,跳过目录和测试变得轻而易举。这不仅能确保测试顺利执行,还能让你的测试套件更精简,加速持续集成 / 持续交付(CI/CD)流程。
在本文中,你将学习如何使用 Pytest 的忽略目录功能,通过不同方法高效地从测试套件中排除不必要的测试。
你将学到什么
本文将教会你:
如何使用各种选项(如命令行标志和配置文件)在 Pytest 中忽略测试目录
如何使用标记忽略单个测试
理解 Pytest 中的测试收集机制
在探讨核心内容之前,让我们先了解一下 Pytest 是如何收集测试的。
Pytest 会从项目的根目录开始,对所有后续目录进行递归搜索,查找与配置模式匹配的文件和目录。具体如下:
它会识别以 “test” 或 “tests” 为前缀的目录。你可以通过配置文件(如 pytest.ini)中的
testpaths
变量来定义测试目录。如果你需要复习一下 pytest.ini,可以查看这篇指南。搜索范围会扩展到名称与测试模式匹配的文件(例如
test_*.py
或*_test.py
)。Pytest 会识别具有指定名称的函数或类,例如函数名以
test_*
开头,或者类名以Test*
开头且包含测试方法。夹具对于准备测试环境至关重要,可通过初始化变量、资源并执行设置和清理操作来实现。你可以在
conftest.py
中使用@pytest.fixture
装饰器来定义夹具。
这些约定是 Pytest 定位测试目录的默认模式。
如果你的项目目录和名称与这些约定不符,可能会遇到 “Collected 0 items” 错误。
你还可以使用自定义模式更改 Pytest 的默认测试发现规则,例如:
[pytest]
python_files = check_*.py
python_classes = Check
python_functions = *_check
为什么要在 Pytest 中忽略目录?
那么,为什么要在 Pytest 中忽略目录呢?为什么不让测试框架自行处理呢?
想想看,大型项目通常包含 1000 多个测试(例如单元测试、集成测试、功能测试、回归测试、冒烟测试、端到端测试),而你需要分析某些特定测试的结果。或者你正在开发一个小功能,不需要运行整个测试套件。
当你运行 pytest 时,它会收集所有测试,这包括仍在开发中的测试、针对即将开发模块的测试,或者与当前任务无关的测试。这会使你的测试套件变得臃肿,运行速度变慢。
你可以根据 Pytest 标记来运行测试,但每次都为每个标记重复操作会很麻烦。此外,通过命令行进行选择性测试执行也具有挑战性。
这时,Pytest 的忽略目录功能就派上用场了,它可以让你干净利落地排除不必要的测试,使其不被执行。
加快测试执行速度:通过排除不必要的目录,你可以减少 Pytest 需要发现和运行的测试数量。这可以显著缩短整体测试设置和执行时间,还能加快 CI/CD 流程,为你节省成本。
聚焦测试:忽略目录可以让你专注于测试项目的核心和相关部分,使测试执行更有针对性。
使测试输出更简洁:忽略目录有助于保持测试结果输出的简洁和有条理。你可以避免测试报告中充斥着无关信息。
简化维护工作:忽略特定目录可以更轻松地维护和更新测试。你可以将测试工作集中在项目的特定区域,而不受无关文件的干扰。
采用这一功能不仅可以优化测试策略,还有助于实现更高效、快速的开发过程。
在了解了忽略目录的好处后,让我们通过一个简单的例子来探讨如何在 Pytest 中实现这一功能。
实际示例
我们的示例代码结构如下:
.
├── .gitignore
├── README.md
├── pytest.ini
├── requirements.txt
├── tests
│ ├── __init__.py
│ ├── in_progress
│ │ └── test_in_progress_modules.py
│ ├── upcoming_modules
│ │ └── test_upcoming_modules.py
│ └── test_converter.py
└── src
└── converter.py
示例代码
我们的示例代码是一个简单的度量单位转换器。
src/converter.py
def centimeter_to_meter(c: float) -> float:
"""
厘米转米的函数
"""
return c / 100
def meter_to_centimeter(m: float) -> float:
"""
米转厘米的函数
"""
return m * 100
def foot_to_inch(f: float) -> float:
"""
英尺转英寸的函数
"""
return f * 12
def inch_to_foot(i: float) -> float:
"""
英寸转英尺的函数
"""
return i / 12
def meter_to_millimeter(i: float) -> float:
"""
米转毫米的函数
开发中
"""
# return i / 12
这里有四个用于转换米、厘米、英尺和英寸的方法,还有一个正在开发中的函数。
测试代码
tests/test_converter.py
from src.converter import (
centimeter_to_meter,
meter_to_centimeter,
foot_to_inch,
inch_to_foot,
)
import pytest
def test_centimeter_to_meter():
assert centimeter_to_meter(100) == 1
def test_meter_to_centimeter():
assert meter_to_centimeter(1) == 100
def test_foot_to_inch():
assert foot_to_inch(1) == 12
def test_inch_to_foot():
assert inch_to_foot(12) == 1
@pytest.mark.skip(reason="Function not implemented yet")
def test_millimeter_to_centimeter():
assert millimeter_to_centimeter(10) == 1
@pytest.mark.xfail(reason="Function expected to fail")
def test_centimeter_to_millimeter():
assert centimeter_to_millimeter(10) == 1
注意,我们在 test_millimeter_to_centimeter()
函数上使用了 @pytest.mark.skip
标记,因为目标函数尚未完成。
此外,我们在 test_centimeter_to_millimeter()
函数上标记了 @pytest.mark.xfail
,因为我们还没有开发 centimeter_to_millimeter()
函数,预计该测试会失败,或者可能存在需要解决的 bug。
为了模拟测试排除,让我们创建一个in_progress
文件夹,用于存放仍在开发中的测试,再创建一个 upcoming_modules
文件夹,用于存放我们计划不久后开发的函数的测试。
tests/in_progress/test_in_progress_modules.py
# 此目录包含正在开发中的模块的测试。
# 开发尚未完成。因此我们可以跳过这个目录。
def test_meter_to_millimeter():
pass
tests/upcoming_modules/test_upcoming_modules.py
# 即将开发的函数
def test_kilometer_to_meter():
pass
# 即将开发的函数
def test_meter_to_kilometer():
pass
在 Pytest 中忽略目录 / 测试的三种方法
使用命令行选项
跳过不需要的测试目录最简单的方法是使用 Pytest 的 --ignore
命令行选项。这个选项允许你在测试期间排除特定的目录。
对于我们的示例代码,你可以这样编写命令行命令:
pytest --ignore=tests/in_progress --ignore=tests/upcoming_modules
要忽略目录中的某些文件,你可以使用以下命令行命令:
pytest --ignore=tests/in_progress/test_in_progress_modules.py
这里,我们只忽略了 tests/in_progress
目录中的 test_in_progress_modules.py
文件。
这种方法适用于你只想临时忽略某个特定目录的情况。但显然,它不是永久排除目录的最佳选择,而且每次都要记住这些命令会很麻烦。
使用配置文件
当需要排除多个目录时,选择配置文件是比编写冗长的命令行指令更方便的方法。
利用 Pytest 的默认配置文件 pytest.ini
,你可以轻松指定要忽略的目录,如下所示:
[pytest]
testpaths =
tests
integration
addopts = -v
# 指定要忽略的目录
norecursedirs = tests/in_progress tests/upcoming_modules
这里我们使用了 norecursedirs
关键字来忽略目录。但你也可以使用 addopts
关键字来实现相同的效果。
addopts = --ignore=tests/in_progress --ignore=tests/upcoming_modules
要忽略目录中的某些文件,你可以使用:
addopts = --ignore=tests/in_progress/test_in_progress_modules.py
这里,我们只忽略了 tests/in_progress
目录中的 test_in_progress_modules.py
文件。
当你需要永久从测试中排除目录时,这种方法很有用。
除了 pytest.ini
,你还可以使用其他配置文件,如 setup.cfg
、tox.ini
和 pyproject.toml
来定义 Pytest 配置。以下指南展示了如何使用各种文件定义 Pytest 配置。
使用标记(针对单个测试)
标记是对测试进行分类和定制的便捷工具。作为注解,标记可以附加到函数、方法或类上,为你的测试提供额外的信息。
要处理单个测试,你可以应用 @pytest.mark.skip
或 @pytest.mark.xfail
等标记。
@pytest.mark.skip
装饰器表示跳过该测试,而 @pytest.mark.xfail
标记表示该测试预计会失败。
在我们的示例代码中:
tests/test_converter.py
import pytest
@pytest.mark.skip(reason="Function not implemented yet")
def test_millimeter_to_centimeter():
assert millimeter_to_centimeter(10) == 1
@pytest.mark.xfail(reason="Function expected to fail")
def test_centimeter_to_millimeter():
assert centimeter_to_millimeter(10) == 1
这使你可以根据自己的需求控制特定测试的行为。
当你需要经常忽略某些测试或目录时,我建议使用配置文件。这种方法可以避免每次都输入冗长的命令行指令。然而,当需要忽略单个测试时,使用标记是最佳选择。
你还可以应用自定义标记,如 @pytest.mark.smoke
、@pytest.mark.regression
或 @pytest.mark.slow
来对你的测试进行分类,
运行测试
你可以使用以下命令运行测试:
pytest
或者,你也可以通过命令行命令忽略目录:
pytest -v --ignore=tests/in_progress --ignore=tests/upcoming_modules
这两种方法会产生相同的输出:
pytest-ignore-directory-example-result
你会看到有 1 个测试被跳过,1 个测试预计会失败,这对应了我们测试代码中前面提到的测试。此外,tests/in_progress
和 tests/upcoming_modules
目录被忽略了,因为我们通过命令行或配置文件指示 Pytest 这样做。
为代码覆盖率忽略文件
Pytest 覆盖率报告提供了已验证代码的百分比度量。要生成覆盖率报告,你可以使用 pytest-cov
或 coverage
插件,它们可以在终端中生成报告,也可以生成美观的 HTML 文件,甚至可以在你的 GitHub 仓库上显示一个漂亮的徽章。
许多公司在拉取请求(PR)或持续集成(CI)过程中要求进行覆盖率报告检查。你可能会认为覆盖率报告只是衡量代码质量的一种表面指标,但它是确保你编写的代码有良好测试覆盖率的好方法。
如果你需要从覆盖率报告中排除特定的目录或文件,可以打开 .coveragerc
文件。使用 omit
关键字指定你要忽略的目录或文件。以下是一个示例:
[run]
omit =
# 忽略 /in_progress 目录下的所有内容
/in_progress/*
# 忽略这个单个文件
tests/simple_tests.py
在上述配置中,我们排除了整个 /in_progress/*
目录以及测试文件 tests/simple_tests.py
。请注意,这些路径是相对于项目根目录的。
这种灵活性使你能够根据项目的特定需求自定义覆盖率报告。这篇关于 Pytest 代码覆盖率的综合教程将教你如何实现代码覆盖率。
忽略目录时的常见问题
在忽略目录时,你可能会遇到以下挑战:
遗漏重要测试:在不检查目录内测试的情况下忽略目录,可能会导致排除关键测试,从而将潜在的 “有缺陷代码” 发布到生产环境中。
测试碎片化:忽略目录可能会导致测试过程出现中断或漏洞。在为了提高效率而排除不必要的测试和确保全面的测试覆盖之间取得平衡至关重要。
保持配置一致性:在协作的 CI/CD 环境中,确保不同开发环境中被忽略目录的配置一致至关重要。
目录结构变化:当项目结构频繁变化时,被忽略的目录可能会带来挑战。在这种情况下,可能需要调整测试配置以适应不断变化的项目布局。
复杂的配置管理:随着被忽略目录数量的增加,管理配置文件和命令行参数可能会变得更加复杂。有效地应对这种复杂性对于保持简化的测试过程至关重要。
忽略目录的最佳实践
让我们来了解一些在忽略目录时应遵循的技巧和方法:
利用配置文件:当需要永久排除目录时,选择配置文件。这样可以确保每次运行测试时自动排除不必要的目录,而不像命令行命令那样容易被忘记或输入错误。
记录被忽略的目录:清楚地记录为什么要忽略某些目录。为从测试中排除特定目录提供详细的解释,为团队提供有价值的上下文信息。
保持 CI/CD 管道的一致性:通过采用标准配置来标记被忽略的目录,确保 CI/CD 管道的一致性。这种方法可以提高测试的可靠性和可重复性。
考虑测试依赖关系:注意不同目录中测试之间的依赖关系。忽略一个目录可能会对其他目录中的测试产生影响。理想情况下,测试应该是独立且分离的。如果你需要共享夹具,可以在本地目录中创建一个
conftest.py
文件并进行共享。定期审查和更新:随着项目的发展,某些目录可能会重新变得与测试相关。定期审查和更新被忽略目录的列表,以使其与项目不断变化的需求保持一致。
通过采用这些最佳实践,你可以有效地利用 Pytest 的忽略目录功能,确保维护一个强大而全面的测试套件。
总结
我们对 Pytest 忽略目录功能的探索就到这里。
本文简要介绍了在 Pytest 中忽略目录的方法,并强调了它在开发过程中的好处。通过一个实际示例,你了解了如何通过不同的方法(命令行、配置文件和标记)使用 Pytest 的忽略目录功能。你深入探讨了从代码覆盖率中排除测试的细节,提及了忽略目录相关的常见问题,并分享了一些提高目录排除效率的宝贵最佳实践。
本文提供了三种在测试执行期间忽略目录的方法。如果你只想在单次测试运行中排除某个目录,选择命令行命令是理想的。如果你想永久从测试中排除目录,使用配置文件是正确的选择。此外,标记为轻松跳过单个测试提供了便捷的工具。
希望这些见解能让你更有信心,利用 Pytest 的忽略目录选项来提升测试执行效率!