使用 Python 进行测试(3)pytest setup

2024-06-16 11:20

本文主要是介绍使用 Python 进行测试(3)pytest setup,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

总结

我们前进到更真实的项目:

less_basic_project
├── my_awesome_package
│   ├── calculation_engine.py
│   ├── __init__.py
│   ├── permissions.py
├── pyproject.toml
└── tests├── conftest.py├── test_calculation_engine.py└── test_permissions.py

使用pyproject.toml配置pytest:

[tool.pytest.ini_options] # mandatory section name
addopts = "-s --no-header --no-summary" # force cmd flags
testpaths = [ # what directories contain tests"tests",
]
pythonpath = [ # what to add to the python path"."
]

我们简化了 pytest 调用,以获得自动递归测试发现,解决导入问题并使输出更清晰。
我们还可以利用“conftest.py”来定义整个项目的夹具,并缓存它们的执行:

@pytest.fixture(scope="module") # run once for each test file
def a_fixture():...

即使没有众多的插件,我们也可以使用许多标志来微调 pytest 的行为,其中包括:

  • -x:第一次失败时停止
  • --pdb:失败时启动pdb调试
  • -k <filter>:仅发现与筛选器匹配的测试
  • --ff:从上次运行失败的测试开始
  • --nf:从新文件开始
  • --sw:从上一次运行停止的地方开始
  • --no-header/--no-summary:删除输出中的大块文本
  • --vevbosity=x:控制输出细粒度,从0级到3级。

再说一句就上路

我保证,下一部分我们将回答诸如“要测试什么”之类的实际问题。但在我们继续之前,我们需要再熟悉下我们的工具。
你看,pytest是一个很棒的工具,但目前为止,我只展示了玩具示例,所以当然一切都很好。不过,你会有一个大项目,包含大量文件、导入、配置等。因此,你需要知道如何再复杂地情况下使用pytest。

真实的项目布局

让我们创建一个更可能使用到的文件结构:

less_basic_project
├── my_awesome_package
│   ├── __init__.py
│   └── the_code_to_test.py
├── pyproject.toml
└── tests└── the_tests.py

现在我们有了一个less_basic_project的项目根目录,其中包含两个目录。"tests"目录包含项目的测试,“my_awesome_package"包含项目源代码。此外,__init__.py用于导入。最后,项目根目录下的"pyproject.toml”,是python项目的标准配置文件。

第一个ImportError

如果你走到项目的根目录下,像个好人一样运行pytest tests/the_tests.py,你会摔倒:

$ pytest tests/the_tests.py
...
tests/the_tests.py:4: in <module>from the_code_to_test import add
E   ModuleNotFoundError: No module named 'the_code_to_test'

事实上,the_code_to_test不再是正确的路径,它已经是包的一部分了,我们需要修复"the_tests.py"的import:

from the_code_to_test import add

改为

from my_awesome_package.the_code_to_test import add

现在应该没问题了,你知道的,sys.path会将当前目录添加进来,因此我们能找到my_awssome_package。但事实并非如此,再次运行pytest,再次崩溃:

$ pytest tests/the_tests.py
...
tests/the_tests.py:4: in <module>from my_awesome_package.the_code_to_test import add
E   ModuleNotFoundError: No module named 'my_awesome_package'

wtf
pytest可执行文件是个例外,当前目录没有被添加到sys.path中。如果你使用python -m pytest执行测试,就能正常运行:

python -m pytest tests/the_tests.py
================= test session starts ================
platform linux -- Python 3.10.13, pytest-7.3.0, pluggy-1.0.0
rootdir: /path/to/less_basic_project
plugins: django-4.5.2, clarity-1.0.1
collected 4 itemstests/the_tests.py ....================= 4 . in 0.01s  ================

这就是我推荐使用-m的原因。不幸的是,这会显得非常啰嗦:
python -m pytest -s test/the_tests.py

自动发现测试

pytest可以自动扫描符合命名规则的测试文件,默认的测试文件命是test_xxx格式。
让我们将the_code_to_test.py的功能明确,并重命名为calculation_engine.py,然后将其测试文件也重命名为test_calculation_engine.py,别忘了修改import!

我们得到

less_basic_project
├── my_awesome_package
│   ├── calculation_engine.py
│   ├── __init__.py
├── pyproject.toml
└── tests└── test_calculation_engine.py <- change imports here

现在我们可以运行:

 python -m pytest tests

pytest会自动寻找测试并执行。

下面我们新加一个功能,确保我们有一个安全系统:
my_awesome_package/permissions.py

def can_access(file_path):return True

以及相应的测试 tests/test_permissions.py:

from my_awesome_package.permissions import can_accessdef test_can_access():assert can_access("/")

再次运行:

 python -m pytest tests
================= test session starts ================
platform linux -- Python 3.10.13, pytest-7.3.0, pluggy-1.0.0
rootdir: /path/to/less_basic_project
plugins: django-4.5.2, clarity-1.0.1
collected 5 itemstests/test_calculation_engine.py ....                                 [ 80%]
tests/test_permissions.py .                                           [100%]================= 5 . in 0.01s  ================

pytest会自动寻找测试并执行。

Pytest 配置

Pytest 可以从“pyproject.toml”文件配置,可以将所需的默认行为放在那里,命令行中的内容都会覆盖此默认配置。
下面是一个例子:

[tool.pytest.ini_options] # mandatory section name
addopts = "-s" # force a command line option
testpaths = [ # what directories contain tests"tests",
]
pythonpath = [ # what to add to the python path"."
]

有了这个,你就可以直接使用pytest了:

pytest
================= test session starts ================
platform linux -- Python 3.10.13, pytest-7.3.0, pluggy-1.0.0
rootdir: /path/to/less_basic_project
plugins: django-4.5.2, clarity-1.0.1
collected 5 items
tests/test_calculation_engine.py::test_add_integers
This is run before each test
.
We tested with 5This is run after each testtests/test_calculation_engine.py::test_add_strings
This is run before each test
.
This is run after each testtests/test_calculation_engine.py::test_add_floats .
tests/test_calculation_engine.py::test_add_mixed_types .
tests/test_permissions.py::test_can_access .

配置文件将当前目录(.)添加到sys.path,在"test"目录中查找测试,添加-s选项输出到命令行。

请注意,pythonpath选项在pytest的版本7中添加,如果你的Pytest版本较低,可能需要额外的插件。此外,其他工具可能也有类似的sys.path问题。最后,你可能想咬紧牙关,为整个项目完成PATHONPATH。但这是另一个话题,我们现在继续关注pytest。

还要考虑到在许多情况下,例如 CI、任务运行器(doit、nox…)、git 钩子,我仍然会使用 python -m pytest ,来避免其他问题。因此,虽然手动执行很方便,但请记住 -m 是我们的救世主。

更多配置

pytest 是充分可配置的,运行 pytest -h 会得到一面充满选项、标志和 env var 的文本墙,您可以使用它来调整其行为。一旦你确信你了解了这个库是如何工作的,你绝对应该探索这些。同时,我将首先指出几个值得您花时间的旋钮:

  • -x:第一次失败时停止
  • --pdb:失败时启动pdb调试
  • -k <filter>:仅发现与筛选器匹配的测试
  • --ff:从上次运行失败的测试开始
  • --nf:从新文件开始
  • --sw:从上一次运行停止的地方开始
  • --no-header/--no-summary:删除输出中的大块文本
  • --vevbosity=x:控制输出细粒度,从0级到3级。

事实上,对于该系列的其余部分,我将在“pyproject.toml”中设置它:
addopts = "-s --no-header --no-summary"
这将使测试运行更加清晰。

但是,这并不是配置 pytest 的唯一方法。您已经了解了另一个:命名约定。还有一些特定的调用约定。当您想要针对特定测试时,最有用的是在路径中使用“::”,因为您可以执行“pytest path/to/test_file.py::test_function”。
例如,我指向调用test_add_strings,我会使用pytest tests/test_calculation_engine.py::test_add_strings

我说更多配置!

Pytest 带有一个功能齐全的插件系统,每个插件系统都可以更改您的 pytest 配置,还可以添加您可以从命令行或“pyproject.toml”设置的全新设置。一旦它们安装在虚拟环境中,它们就会自动加载,除非您在配置中禁用它们。
例如,如果我 pip install pytest-sugarpytest-sugar插件将安装并在我的下一次测试运行时自动加载,为输出提供进度条:
pytest-sugar
它还将添加 pytest 选项 --force-sugar 。

虽然我觉得 pytest-sugar 很漂亮,但我个人并没有使用它,这只是插件工作原理的一个例子。有大量的插件,有些提供代码覆盖率,有些提供数据库设置,有些rest客户端,有些fake数据…生态系统丰富、强大且非常有用。
配置还有另一面:如何将测试与文本编辑器集成。这本身就是一篇完整的文章,而且有这么多的IDE,有这么多不同的偏好,我们不太可能帮助每个人。我可能至少在 VSCode 上写一篇文章,我还在思考。

更多多多多多配置!!!

是的,conf 在 pytest 中非常疯狂。这是最后一个,我发誓。
如果创建一个名为“conftest.py”的文件并将其放在“tests”目录下,则可以以编程方式影响pytest在python中的工作方式。
我们不会讨论你可以用 conftest.py 做的所有事情,因为它很快就会变得深奥。更不用说您实际上可以创建几个相互级联和覆盖的“conftest.py”。这是一个潘多拉魔盒。

在“conftest.py”中,您可以做的最重要的事情是共享夹具和范围。
想一想我们的夹具:

@pytest.fixture()
def random_number():yolo = random.randint(0, 10)yield yoloprint(f"\nWe tested with {yolo}")

它在在“test_calculation_engine.py”中,我们能在“test_permissions.py”中使用random_number吗?
显然不行。但是,如果您将其移动到“conftest.py”,那么突然间,同层的所有测试都可以访问夹具。

less_basic_project
├── my_awesome_package
│   ├── calculation_engine.py
│   ├── __init__.py
│   ├── permissions.py
├── pyproject.toml
└── tests├── conftest.py├── test_calculation_engine.py└── test_permissions.py

conftest.py中包含:

import random
import pytest@pytest.fixture()
def random_number():yolo = random.randint(0, 10)yield yoloprint(f"\nWe tested with {yolo}")

我们从“test_calculation_engine.py”中删除了该夹具。

这引出了范围(scope),默认情况下,每个使用夹具的测试都会调用一次夹具。
但是,对于某些测试,例如数据库设置、网络连接、文件创建、数据生成等,您可能希望夹具在每组测试中实际运行一次,或者在整个测试运行中实际运行一次。
您可以使用 scope 参数执行此操作:

@pytest.fixture(scope="module") # run once for each test file
def random_number():yolo = random.randint(0, 10)yield yoloprint(f"\nWe tested with {yolo}")

scoped的值可设置为 “function” (默认值)、 “module” (一个 py 文件)、 “package” (整个目录)或 “session” (一次运行)。

如果您不需要 yield 的值,但想要副作用,您还可以autouse强制夹具运行为所有测试运行,即使测试没有使用名为 random_number 的参数:@pytest.fixture(autouse=True)
scope 并且 autouse 可以一起使用,谨慎使用,这很容易过度使用。

关于缓存

出于教学原因,我向你展示了一个干净的项目树,但实际上,如果你现在看一眼你的项目树,它看起来更像是这样(YMMV):

less_basic_project
├── my_awesome_package
│   ├── calculation_engine.py
│   ├── __init__.py
│   ├── permissions.py
│   └── __pycache__
│       ├── calculation_engine.cpython-310.pyc
│       ├── __init__.cpython-310.pyc
│       └── permissions.cpython-310.pyc
├── pyproject.toml
├── .pytest_cache
│   ├── CACHEDIR.TAG
│   ├── .gitignore
│   ├── README.md
│   └── v
│       └── cache
│           ├── lastfailed
│           ├── nodeids
│           └── stepwise
└── tests├── conftest.py├── __pycache__│   ├── conftest.cpython-310-pytest-8.1.1.pyc│   ├── test_calculation_engine.cpython-310-pytest-8.1.1.pyc│   └── test_permissions.cpython-310-pytest-8.1.1.pyc├── test_calculation_engine.py└── test_permissions.py

这是因为 python 和 pytest 都有自己的缓存机制。这些文件不是您应该提交到 VCS 的内容(例如 git),可以删除它们。缓存可以让测试运行得更快,并确保一些功能如--ff可用。

这篇关于使用 Python 进行测试(3)pytest setup的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1066330

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal