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

news2024/11/24 10:41:27

总结

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

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 items


tests/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_access

def 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 items


tests/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 5


This is run after each test


tests/test_calculation_engine.py::test_add_strings
This is run before each test
.
This is run after each test


tests/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 yolo
    print(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 yolo
    print(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 yolo
    print(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可用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1830429.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

283. 移动零 (Swift版本)

题目描述 最容易想到的解法 从后向前遍历, 发现0就通过交换相邻元素将0移动到最后 class Solution {func moveZeroes(_ nums: inout [Int]) {for index in (0..<nums.count).reversed() {if nums[index] 0 {moveZeroes(&nums, index)}}}func moveZeroes(_ nums: inout …

Mybatis plus join 一对多语法

一对多案例&#xff08;set集合&#xff09; 1. 实体类 题目 package co.yixiang.exam.entity;import co.yixiang.domain.BaseDomain; import co.yixiang.exam.config.CustomStringListDeserializer; import com.baomidou.mybatisplus.annotation.TableField; import com.fa…

youlai-boot项目的学习—本地数据库安装与配置

数据库脚本 在项目代码的路径下&#xff0c;有两个版本的mysql数据库脚本&#xff0c;使用对应的脚本就安装对应的数据库版本&#xff0c;本文件选择了5 数据库安装 这里在iterm2下使用homebrew安装mysql5 brew install mysql5.7注&#xff1a;记得配置端终下的科学上网&a…

PHP7 数组的实现

前提 PHP版本&#xff1a;php7.0.29使用到的文件 php-src/Zend/zend_types.hphp-src/Zend/zend_hash.hphp-src/Zend/zend_hash.cphp-src/Zend/zend_string.h 本文 是《PHP7底层设计和源码实现》第5章 数组的实现&#xff0c;学习笔记 功能分析 整体结构 bucket 里面增加h字段…

android studio CreateProcess error=2, 系统找不到指定的文件

【问题记录篇】 在AndroidStudio编译开发jni相关工程代码的时候&#xff0c;编译遇到的这个报错&#xff1a; CreateProcess error2, 系统找不到指定的文件。排查处理步骤: 先查看Build Output的具体日志输出 2.了解到问题出在了NDK配置上&#xff0c;此时需要根据自己的gra…

RTOS实时操作系统

常见的RTOS有&#xff1a; VxWorks&#xff1a;广泛应用于工业、医疗、通信和航空航天领域。FreeRTOS&#xff1a;一个开源的RTOS&#xff0c;广泛用于嵌入式设备。uc/OS&#xff1a;一个适用于教育和小型商业项目的RTOS。QNX&#xff1a;主要应用于汽车和工业自动化领域。Win…

第 2 章:Spring Framework 中的 IoC 容器

控制反转&#xff08;Inversion of Control&#xff0c;IoC&#xff09;与 面向切面编程&#xff08;Aspect Oriented Programming&#xff0c;AOP&#xff09;是 Spring Framework 中最重要的两个概念&#xff0c;本章会着重介绍前者&#xff0c;内容包括 IoC 容器以及容器中 …

Android-茫茫9个月求职路,终于拿满意offer

线程和进程的区别&#xff1f;为什么要有线程&#xff0c;而不是仅仅用进程&#xff1f;算法判断单链表成环与否&#xff1f;如何实现线程同步&#xff1f;hashmap数据结构&#xff1f;arraylist 与 linkedlist 异同&#xff1f;object类的equal 和hashcode 方法重写&#xff0…

免费分享:2017-2021全球10m土地利用数据(esri)(附下载方法)

美国环境系统研究所公司&#xff08;Environmental Systems Research Institute, Inc. 简称ESRI公司&#xff09;&#xff0c;以其先进的ArcGIS解决方案&#xff0c;为全球各行业提供多层次、可扩展、功能强大且开放性强的GIS技术。哨兵2号&#xff08;Sentinel-2&#xff09;是…

国产MCU芯片(2):东软MCU概览及触控MCU

前言: 国产芯片替代的一个主战场之一就是mcu,可以说很多国内芯片设计公司都打算或者已经在设计甚至有了一款或多款的量产产品了,这也是国际大背景决定的。过去的家电市场、过去的汽车电子市场,的确国产芯片的身影不是很常见,如今不同了,很多fabless投身这个行业,一种是…

【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)

&#x1f525;引言 本篇将深入解析单链表:探索底层逻辑&#xff0c;理解底层是如何实现并了解该接口实现的优缺点&#xff0c;以便于我们在编写程序灵活地使用该数据结构。 &#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &…

3. ceph-mimic版本部署

ceph-mimic版本部署 一、ceph-mimic版本部署1、环境规划2、系统基础环境准备2.1 关闭防火墙、SELinux2.2 确保所有主机时间同步2.3 所有主机ssh免密2.4 添加所有主机解析 3、配置ceph软件仓库4、安装ceph-deploy工具5、ceph集群初始化6、所有ceph集群节点安装相关软件7、客户端…

【C++】多态|原理|override|final|抽象类|多继承虚函数表|对象模型|虚表打印|(万字详解版)

目录 ​编辑 一.多态的概念 二.多态的构建 虚函数 重写 虚函数重写的例外 协变 隐藏 析构函数的重写 三.重载、重写(覆盖)、隐藏(重定义)的对比 四.C11新增的 override 和 final override final 五.抽象类 六.多态的原理 虚函数表 总结&#xff1a; 引用…

CMake多行注释以及通过Message打印不同级别日志

1 CMake注释 1.1 单行注释 CMake中单行注释时以 # 开头。 # 指定CMake最低版本cmake_minimum_required(VERSION 3.20)# 这是注释project(myproject)1.2 多行注释 多行注释时&#xff0c;以 #[[ 开头&#xff0c;以 ]] 结尾&#xff0c;中间都可以写注释内容。3.0之前的版本…

MySQL面试重点-1

1. 数据库基础知识&#xff1a; DDL、DML、DQL、DCL的概念与区别&#xff1f; DDL&#xff08;数据定义语言&#xff09;&#xff1a;创建&#xff08;CREATE&#xff09;数据库中的各种对象&#xff1a;表、视图、索引等DML&#xff08;数据操纵语言&#xff09;&#xff1a…

点积和叉积

文章目录 1、向量的点积2、向量的叉积3、矩阵的点积4、矩阵的叉积 1、向量的点积 数量积又称标量积&#xff08;Scalar product&#xff09;、点积&#xff08;Dot product&#xff09;&#xff0c;在欧几里得空间&#xff08;Euclidean space&#xff09;中称为内积&#xff…

为何云原生是未来?企业IT架构的颠覆与重构(上)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《未来已来&#xff1a;云原生之旅》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、什么是云原生 2、云原生的背景和起源 背景 起源 关…

酷开会员丨酷开系统K歌模式,父亲节的家庭欢聚时光

K歌以其独特的魅力&#xff0c;为家庭娱乐带来了无限乐趣。想象一下&#xff0c;父亲节这天&#xff0c;打开电视进入K歌频道&#xff0c;与家人一起嗨唱&#xff0c;客厅里充满了欢声笑语&#xff0c;酷开系统的K歌应用也就成为了连接亲情的桥梁&#xff0c;让爸爸们都能在这个…

CSS-0_1 CSS和层叠(样式优先级、内联样式、选择器 用户代理样式)

CSS 的本质就是声明规则 ——《深入解析CSS》 文章目录 CSS层叠和优先级用户代理样式请和用户代理样式和谐相处 选择器单选择器的优先级选择器组的优先级关于选择器的其他源码顺序尽可能的选择优先级低的选择器 内联样式内联样式和JavaScript !important多个 !important 碎碎念…

Wing FTP Server v7.2.0 解锁版安装教程 (跨平台的专业FTP服务器软件)

前言 Wing FTP Server是一款跨平台的专业FTP服务器软件, 支持可扩展处理器架构采用异步IO处理, 在速度和效率方面领先于其他同类产品. 它在高负载的情况下也能持续地正常运行, 非常适合企业文件传输. 通过基于Web管理端, 何时何地都能轻松管理远程的服务器. 除了基本功能外, 它…