综合编译|TesterHome 社区
作者|Rafał Buczyński

一、简介

在 Python 中创建、组织和执行测试。pytest 的独特之处在于它的简单性、可扩展性以及处理复杂测试需求的能力。它支持固定装置、自动测试发现和丰富的插件,以及其他显着的功能,这使其成为从小型到大型应用程序的任何规模项目的可行选择。

文档:http://docs.pytest.org/en/latest/contents.html#ttoc

第三方库:https://pypi.org/search/?g=pytest

Github 地址:https://github.com/pytest-dev/pytest/

二. 安装与配置

在深入使用 pytest 进行测试之前,正确设置至关重要。pytest 的安装和配置过程非常简单,使其成为各个级别的开发人员都可以使用的工具。

如何安装 Pytest

Pytest 可以使用 pip 来安装,pip 是 Python 的包安装程序。建议在安装 pytest 之前创建一个虚拟环境,因为这种做法有助于保持不同项目所需的依赖关系分离和组织。可以这样做:

1.首先,在项目目录中创建一个虚拟环境:

  1. 激活虚拟环境,在 Windows 上使用:

在 Unix 或 MacOS 上使用:

3.激活虚拟环境后,使用 pip 安装 pytest:

配置测试环境

配置 pytest 轻而易举。然而,了解一些基础知识将帮助你从一开始就充分利用它。Pytest 需要知道在哪里可以找到你的测试以及应该考虑什么作为测试。

以下是一些基础知识:

1.命名约定:默认情况下,pytest 会将任何前缀为 test_ 或后缀为 test.py 的文件视为测试文件。同样,这些文件中任何以 test 为前缀的函数都被视为测试函数。遵循这一命名约定将使你的测试井井有条,易于识别。

2.目录结构:虽然 pytest 的目录结构非常灵活,但在项目中通常会有一个专门的测试目录,存放所有测试文件。这样可以将测试与实际代码分开,是一种很好的做法。

项目结构:

  1. 配置文件:对于更高级的配置,pytest 支持名为 pytest.ini、pyproject.toml 或 tox.ini 的配置文件。在该文件中,你可以指定各种选项,如添加命令行参数、设置 python 路径等。

pytest.ini 文件示例:

在这个配置文件中,我们指定了 pytest 的最低版本,添加了一些命令行选项,并将 pytest 指向测试目录。

按照上述步骤,你将拥有一个组织有序、配置完备的测试环境,为你的 pytest 之旅做好准备。在随后的章节中,当你深入研究 pytest 的特性和功能时,这个设置将为你提供良好的服务。

三、Pytest 基础知识

使用 pytest 开始测试,首先要了解它的基本功能。下面的内容旨在提供一个基础,你可以在此基础上随着你的进步构建更复杂的测试场景。这里将介绍的三个主要方面包括:创建简单的功能测试、利用断言以及从命令行识别和运行测试。

创建简单的功能测试

功能测试是一种黑盒测试,通过提供输入并检查输出来测试应用程序的功能。Pytest 简化了创建功能测试的过程。让我们深入研究一个基本示例:

假设在名为 math_operations.py 的文件中有一个函数 add:

要测试该 add 函数,请创建一个名为的新文件 test_math_operations.py 并定义一个函数 test_add:

在此示例中,test_add 是一个简单的功能测试,用于检查 add 函数是否返回正确的输出。

在 Pytest 中使用断言

断言是程序员在代码中假定为真的条件或布尔表达式。Pytest 使断言的使用变得轻松易读。断言语句用于在给定条件为真的情况下继续执行。如果 assert 判断为 false,pytest 将停止测试执行,报告失败,并提供深入的回溯信息。让我们扩展一下前面的例子:

在这里,我们添加了更多断言来检查 add 具有不同输入的函数,确保其在这些场景中的行为符合预期。

从命令行识别并运行测试

Pytest 提供了用于运行测试的命令行界面。编写测试后,运行它们就像在终端中执行单个命令一样简单。导航到包含测试文件的目录,然后运行以下命令:

Pytest 将自动发现当前目录和子目录中遵循测试文件命名约定的所有文件,然后运行它们包含的测试。

要进行更精细的控制,可以指定要运行的文件、测试类甚至测试方法。例如,要只运行 test_math_operations.py 文件中的 test_add 方法,可以执行:

这种命令行灵活性使您可以轻松运行单个测试、文件中的所有测试或项目中的所有测试,随着测试套件的增长,这会非常有帮助。

pytest 用于创建、断言和运行测试的简单而强大的功能使其成为 Python 开发人员珍视的工具。这些基础知识将作为探索更高级测试概念的垫脚石,我们将在后面的文章中介绍这些概念。

四. Pytest 中的固定装置

pytest 的一个重要特点是使用固定装置(fixtures)来创建一个固定的基线,使测试能在这个基线上可靠地重复执行。固定装置是为测试功能创建设置和拆卸代码的一种方法。这种设置可以是以数据为中心的,也可以是在多个测试中重复使用的对象。

夹具(Fixtures)介绍

在 pytest 中,固定装置为创建可在多个测试中共享的设置代码提供了一种方法。一个固定装置函数会为测试创建一个固定的基线,然后固定装置名称会被用作任何测试的输入参数。真正的优势来自于在一个或多个独立的 conftest.py 文件中定义固定装置的能力,从而使它们可用于多个测试文件。

这是一个返回字典的简单装置,可用于保存测试数据:

在测试中,使用夹具名称作为输入参数:

使用夹具进行安装和拆卸的示例

夹具的用途非常广泛,可以在测试开始时进行设置,在测试结束时进行拆卸,以及其间的所有操作。这是一个更复杂的夹具示例,演示了设置和拆卸功能:

假设你有一个数据库类:

你可以在 conftest.py 文件中创建一个固定装置来处理数据库连接的设置和拆卸:

然后,在测试函数中使用该夹具 db_connection:

在此示例中,db_connection 夹具处理创建新对象并连接到数据库的设置 Database,以及在测试函数执行后关闭数据库连接的拆卸。

该 yield 语句是 pytest 从设置阶段过渡到拆卸阶段的地方。之前的所有代码 yield 都是设置,之后的代码都是拆卸。

夹具提供了一种强大的方法来管理测试的设置和拆卸代码,使测试保持干净和干燥(不要重复)。它们提高了代码的可重用性,并且可以显着减少样板代码的数量,使测试更易于编写、阅读和维护。

五. 参数化测试

强大的测试框架应该有助于根据各种输入验证功能或方法,以确保其正确性。Pytest 通过参数化测试的能力在这方面表现出色。参数化有助于使用不同的输入值多次执行测试,这是确认代码在不同条件下的功能和可靠性的关键实践。

如何参数化测试

pytest 中的参数化是使用@pytest.mark.parametrize 装饰器实现的。该装饰器允许指定测试的输入值和预期结果,使其成为增强测试套件全面性的强大工具。以下是如何使用它:

假设有一个函数 multiply 想要使用不同的输入集进行测试:

可以创建一个测试文件 test_math_operations.py 并使用@pytest.mark.parametrize 装饰器来测试 multiply 具有各种输入的函数:

在上面的代码中,a、b、 和 expected 是函数的输入参数 test_multiply,列表中的元组为测试的每次迭代提供这些参数的值。

不同测试用例的参数化示例

参数化可以在多种场景中使用。这是另一个说明性示例,其中可以采用参数化来测试确定数字是否为偶数的函数:

要测试该 is_even 函数,您可以使用参数化来验证其在一系列数字上的正确性:

在这个示例中,test_is_even 函数以不同的数值和预期参数值运行了 6 次,确保 is_even 函数对正数和负数(包括零)都表现出预期的效果。

在 pytest 中参数化测试的能力增强了测试套件的清晰度和全面性。通过利用参数化,可以确保您的函数和方法在广泛的输入中得到彻底的测试,从而培养出强大而可靠的代码库。

六. 高级功能

随着你越来越习惯 pytest 的基础知识,探索其高级功能可以进一步优化你的测试过程。下面,让我们将深入研究 pytest 中的标记、插件以及异常和模拟的处理。尽管被称为高级功能,但这些功能非常易于使用,并且可以显着增强测试能力。

标记及其用途

pytest 中的标记提供了一种将元数据添加到测试中的方法,允许根据标记运行测试子集。这对于具有大量测试套件的大型项目特别有用。以下是使用标记的方法:

使用配置文件定义自定义标记 pytest.ini:

现在,将@pytest.mark.slow装饰器应用于你想要标记为慢的任何测试:

要仅运行慢速测试,请使用-mpytest 选项:

插件以及与其他工具的集成

pytest 的插件架构允许扩展其功能并与其他工具集成。有许多可用的插件,创建自己的插件也是一个简单的过程。这是使用该 pytest-cov 插件来测量代码覆盖率的示例:

通过 pip 安装 pytest-cov:

现在,运行 pytest 并--cov 选择获取覆盖率报告:

此命令将运行测试并提供 myproject 目录的覆盖率报告。

测试异常和使用模拟

测试代码如何处理异常对于构建健壮的应用程序至关重要。Pytest 简化了这个过程。以下是测试函数是否引发预期异常的方法:

模拟是另一个高级功能,可以在测试期间模拟行为,这对于将测试与外部因素或系统隔离非常有用。该 unittest.mock 库包含在 Python 中,可以与 pytest 一起使用来创建模拟。

下面是使用 mock 来模拟数据库连接的示例:

在此示例中,Mock 用于将实际 db_connection 函数替换为模拟版本,这个模拟版本在调用时返回 “Connected”,从而允许进行受控和隔离测试。

探索 pytest 的这些高级功能为更有效的测试开辟了多种可能性。通过利用标记、插件以及测试异常和使用模拟的功能,从而可以构建更加健壮、可靠和可维护的代码库。

七. 实际用例

通过深入研究 pytest 用于测试的现实场景,可以很好地补充 pytest 的理论知识。下面将展示在实际项目中实施测试的示例,然后讨论使用 pytest 测试软件时采用的最佳实践。

在实际项目中实施测试的示例

1.测试 REST API:

使用 REST API 时,测试端点的预期行为至关重要。让我们考虑一个简化的场景,其中您有一个 Flask 应用程序,该应用程序具有返回问候语的单个端点:

可以创建一个测试文件 test_app.py,使用 pytest 和 requests 库测试 /greet 端点:

  1. 测试数据库交互:

确保应用程序按预期与数据库交互至关重要。假设有一个查询数据库以按 ID 检索用户的函数:

可以使用 pytest 装置来管理数据库连接和回滚事务,以确保测试状态一致:

使用 Pytest 进行软件测试的最佳实践的讨论

遵循软件测试的最佳实践不仅可以提高代码质量,还可以使测试过程更加高效和有效。以下是使用 pytest 时的一些推荐做法:

1.保持测试简单且集中:每个测试都应该验证一个行为或概念。保持测试简单且集中,可以更轻松地识别和解决出现的问题。

2.使用描述性测试函数名称:描述性名称有助于理解测试的目的。采用清晰表明每个测试正在检查的内容的命名约定。

3.利用夹具进行安装和拆卸:利用 pytest 夹具来管理安装和拆卸操作。这促进了代码重用并有助于保持测试 DRY(不要重复自己)。

4.采用参数化来覆盖一系列输入:参数化测试有助于使用最少的代码针对广泛的输入值验证代码的行为。

5.维护一致的目录结构:遵循一致的目录结构可以更轻松地查找测试、装置和其他相关文件。

6.定期运行测试并自动化:定期运行测试有助于及早发现回归。尽可能自动化你的测试过程以确保一致性。

7.投入时间学习高级功能:Pytest 提供许多高级功能,例如标记、插件和模拟。学习这些可以显着提高您的测试能力。

在项目中使用 pytest 时结合这些最佳实践将培养质量保证文化,并为项目的成功和可维护性做出重大贡献。

八、结论

通过上面的介绍,带领大家了解了用于设置和拆卸的固定装置的概念,参数化测试以涵盖一系列情况,并深入研究了一些高级功能,例如使用标记、插件以及处理异常和模拟,所有这些都在 pytest 生态系统中实现的。

当你超越基础知识时,丰富的资源和充满活力的社区正在等待着支持您的 pytest 工作。参与 pytest 社区,参与讨论,做出贡献,并继续探索 pytest 提供的无数功能。官方文档是一个信息宝库,并且有许多插件可用于进一步扩展 pytest 的功能。

此外,各种博客、论坛和 GitHub 等平台托管了大量使用 pytest 的项目,提供了真实的示例和创新用例。这个丰富的生态系统不仅促进学习,还提供协作和为开源项目做出贡献的机会。(原文链接:https://python.plainenglish.io/pytest-in-practice-fundamentals-of-python-testing-techniques-69f299aef09e


↙↙↙阅读原文可查看相关链接,并与作者交流