可能是 Python 中最火的第三方开源测试框架 pytest

news2024/12/26 9:21:14

一、介绍

本篇文章是《聊聊 Python 的单元测试框架》的第三篇,前两篇分别介绍了标准库 unittest 和第三方单元测试框架 nose。作为本系列的最后一篇,压轴出场的是Python 世界中最火的第三方单元测试框架:pytest。

pytest 项目地址:

https://github.com/pytest-dev/pytest

它有如下主要特性:

  • assert 断言失败时输出详细信息(再也不用去记忆 self.assert* 名称了)
  • 自动发现 测试模块和函数
  • 模块化夹具 用以管理各类测试资源
  • unittest 完全兼容,对 nose 基本兼容
  • 非常丰富的插件体系,有超过 315 款第三方插件,社区繁荣

和前面介绍 unittestnose 一样,我们将从如下几个方面介绍 pytest 的特性。

如果你想学习Pytest自动化测试框架,我这边给你推荐一套视频,这个视频可以说是B站百万播放全网第一的自动化测试教程,同时在线人数到达1000人,并且还有笔记可以领取及各路大神技术交流:扣扣群:798478386

2023最新pytest接口自动化测试框架,三天带你精通pytest,带你写出最好的代码!(已更新2023新版)_哔哩哔哩_bilibili2023最新pytest接口自动化测试框架,三天带你精通pytest,带你写出最好的代码!(已更新2023新版)共计32条视频,包括:1、Pytest简介、2、Pytest之pytest测试用例的运行方式、3、Pytest之pytest执行测试用例的顺序是怎样的呢?等,UP主更多精彩视频,请关注UP账号。https://www.bilibili.com/video/BV1py4y1t7bJ/?spm_id_from=333.337.search-card.all.click

二、用例编写

nose 一样,pytest 支持函数、测试类形式的测试用例。最大的不同点是,你可以尽情地使用 assert 语句进行断言,丝毫不用担心它会在 noseunittest 中产生的缺失详细上下文信息的问题。

比如下面的测试示例中,故意使得 test_upper 中断言不通过:

import pytest

def test_upper():
    assert 'foo'.upper() == 'FOO1'

class TestClass:
    def test_one(self):
        x = "this"
        assert "h" in x

    def test_two(self):
        x = "hello"
        with pytest.raises(TypeError):
            x + []

而当使用 pytest 去执行用例时,它会输出详细的(且是多种颜色)上下文信息:

=================================== test session starts ===================================
platform darwin -- Python 3.7.1, pytest-4.0.1, py-1.7.0, pluggy-0.8.0
rootdir: /Users/prodesire/projects/tests, inifile:
plugins: cov-2.6.0
collected 3 items

test.py F..                                                                         [100%]

======================================== FAILURES =========================================
_______________________________________ test_upper ________________________________________

    def test_upper():
>       assert 'foo'.upper() == 'FOO1'
E       AssertionError: assert 'FOO' == 'FOO1'
E         - FOO
E         + FOO1
E         ?    +

test.py:4: AssertionError
=========================== 1 failed, 2 passed in 0.08 seconds ============================

难看到,pytest 既输出了测试代码上下文,也输出了被测变量值的信息。相比于 noseunittestpytest 允许用户使用更简单的方式编写测试用例,又能得到一个更丰富和友好的测试结果。

三、用例发现和执行

unittestnose 所支持的用例发现和执行能力,pytest 均支持。 pytest 支持用例自动(递归)发现:

  • 默认发现当前目录下所有符合 test_*.py*_test.py 的测试用例文件中,以 test 开头的测试函数或以 Test 开头的测试类中的以 test 开头的测试方法
    • 使用 pytest 命令
  • nose2 的理念一样,通过在配置文件中指定特定参数,可配置用例文件、类和函数的名称模式(模糊匹配)

pytest 也支持执行指定用例:

  • 指定测试文件路径
    • pytest /path/to/test/file.py
  • 指定测试类
    • pytest /path/to/test/file.py:TestCase
  • 指定测试方法
    • pytest another.test::TestClass::test_method
  • 指定测试函数
    • pytest /path/to/test/file.py:test_function

四、测试夹具(Fixtures)

pytest 的测试夹具和 unittestnosenose2的风格迥异,它不但能实现 setUptearDown这种测试前置和清理逻辑,还其他非常多强大的功能。

4.1 声明和使用

pytest 中的测试夹具更像是测试资源,你只需定义一个夹具,然后就可以在用例中直接使用它。得益于 pytest 的依赖注入机制,你无需通过from xx import xx的形式显示导入,只需要在测试函数的参数中指定同名参数即可,比如:

import pytest


@pytest.fixture
def smtp_connection():
    import smtplib

    return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)


def test_ehlo(smtp_connection):
    response, msg = smtp_connection.ehlo()
    assert response == 250

上述示例中定义了一个测试夹具 smtp_connection,在测试函数 test_ehlo 签名中定义了同名参数,则 pytest 框架会自动注入该变量。

4.2 共享

pytest 中,同一个测试夹具可被多个测试文件中的多个测试用例共享。只需在包(Package)中定义 conftest.py 文件,并把测试夹具的定义写在该文件中,则该包内所有模块(Module)的所有测试用例均可使用 conftest.py 中所定义的测试夹具。

比如,如果在如下文件结构的 test_1/conftest.py 定义了测试夹具,那么 test_a.pytest_b.py 可以使用该测试夹具;而 test_c.py 则无法使用。

`-- test_1
|   |-- conftest.py
|   `-- test_a.py
|   `-- test_b.py
`-- test_2
    `-- test_c.py

4.3 生效级别

unittestnose 均支持测试前置和清理的生效级别:测试方法、测试类和测试模块。

pytest 的测试夹具同样支持各类生效级别,且更加丰富。通过在 pytest.fixture 中指定 scope 参数来设置:

  • function —— 函数级,即调用每个测试函数前,均会重新生成 fixture
  • class —— 类级,调用每个测试类前,均会重新生成 fixture
  • module —— 模块级,载入每个测试模块前,均会重新生成 fixture
  • package —— 包级,载入每个包前,均会重新生成 fixture
  • session —— 会话级,运行所有用例前,只生成一次 fixture

当我们指定生效级别为模块级时,示例如下:

import pytest
import smtplib


@pytest.fixture(scope="module")
def smtp_connection():
    return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)

4.4 测试前置和清理

pytest 的测试夹具也能够实现测试前置和清理,通过 yield 语句来拆分这两个逻辑,写法变得很简单,如:

import smtplib
import pytest


@pytest.fixture(scope="module")
def smtp_connection():
    smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
    yield smtp_connection  # provide the fixture value
    print("teardown smtp")
    smtp_connection.close()

在上述示例中,yield smtp_connection 及前面的语句相当于测试前置,通过 yield 返回准备好的测试资源 smtp_connection; 而后面的语句则会在用例执行结束(确切的说是测试夹具的生效级别的声明周期结束时)后执行,相当于测试清理。

如果生成测试资源(如示例中的 smtp_connection)的过程支持 with 语句,那么还可以写成更加简单的形式:

@pytest.fixture(scope="module")
def smtp_connection():
    with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
        yield smtp_connection  # provide the fixture value

pytest 的测试夹具除了文中介绍到的这些功能,还有诸如参数化夹具、工厂夹具、在夹具中使用夹具等更多高阶玩法,详情请阅读 "pytest fixtures: explicit, modular, scalable"。

五、跳过测试和预计失败

pytest 除了支持 unittestnosetest 的跳过测试和预计失败的方式外,还在 pytest.mark 中提供对应方法:

  • 通过 skip 装饰器或 pytest.skip 函数直接跳过测试
  • 通过 skipif按条件跳过测试
  • 通过 xfail 预计测试失败

示例如下:

@pytest.mark.skip(reason="no way of currently testing this")
def test_mark_skip():
    ...

def test_skip():
    if not valid_config():
        pytest.skip("unsupported configuration")

@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher")
def test_mark_skip_if():
    ...

@pytest.mark.xfail
def test_mark_xfail():
    ...

关于跳过测试和预计失败的更多玩法,参见 "Skip and xfail: dealing with tests that cannot succeed"

六、子测试/参数化测试

pytest 除了支持 unittest 中的 TestCase.subTest,还支持一种更为灵活的子测试编写方式,也就是 参数化测试,通过 pytest.mark.parametrize 装饰器实现。

在下面的示例中,定义一个 test_eval 测试函数,通过 pytest.mark.parametrize 装饰器指定 3 组参数,则将生成 3 个子测试:

@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
def test_eval(test_input, expected):
    assert eval(test_input) == expected

示例中故意让最后一组参数导致失败,运行用例可以看到丰富的测试结果输出:

========================================= test session starts =========================================
platform darwin -- Python 3.7.1, pytest-4.0.1, py-1.7.0, pluggy-0.8.0
rootdir: /Users/prodesire/projects/tests, inifile:
plugins: cov-2.6.0
collected 3 items

test.py ..F                                                                                     [100%]

============================================== FAILURES ===============================================
__________________________________________ test_eval[6*9-42] __________________________________________

test_input = '6*9', expected = 42

    @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
    def test_eval(test_input, expected):
>       assert eval(test_input) == expected
E       AssertionError: assert 54 == 42
E        +  where 54 = eval('6*9')

test.py:6: AssertionError
================================= 1 failed, 2 passed in 0.09 seconds ==================================

若将参数换成 pytest.param,我们还可以有更高阶的玩法,比如知道最后一组参数是失败的,所以将它标记为 xfail:

@pytest.mark.parametrize(
    "test_input,expected",
    [("3+5", 8), ("2+4", 6), pytest.param("6*9", 42, marks=pytest.mark.xfail)],
)
def test_eval(test_input, expected):
    assert eval(test_input) == expected

如果测试函数的多个参数的值希望互相排列组合,我们可以这么写:

@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
def test_foo(x, y):
    pass

上述示例中会分别把 x=0/y=2x=1/y=2x=0/y=3x=1/y=3带入测试函数,视作四个测试用例来执行。

七、测试结果输出

pytest 的测试结果输出相比于 unittestnose 来说更为丰富,其优势在于:

  • 高亮输出,通过或不通过会用不同的颜色进行区分
  • 更丰富的上下文信息,自动输出代码上下文和变量信息
  • 测试进度展示
  • 测试结果输出布局更加友好易读

八、插件体系

pytest 的插件十分丰富,而且即插即用,作为使用者不需要编写额外代码。关于插件的使用,参见"Installing and Using plugins"。

此外,得益于 pytest 良好的架构设计和钩子机制,其插件编写也变得容易上手。关于插件的编写,参见"Writing plugins"。

九、总结

我们罗列一个横向对比表,来总结下这些单元测试框架的异同:

Python 的单元测试框架看似种类繁多,实则是一代代的进化,有迹可循。抓住其特点,结合使用场景,就能容易的做出选择。

若你不想安装或不允许第三方库,那么 unittest 是最好也是唯一的选择。反之,pytest 无疑是最佳选择,众多 Python 开源项目(如大名鼎鼎的 requests)都是使用 pytest 作为单元测试框架。甚至,连 nose2 在官方文档上都建议大家使用 pytest,这得是多大的敬佩呀!

 

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

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

相关文章

预见未来:超强元AI诞生,抓住这个机会,利用AI变现也变得更加容易

目录 一、引言 二、介绍 三、技术展现 四、元AI架构图展现 五、元AI变现技巧—商业版说明 六、后期规划 一、引言 如何利用AI变现已经成为了当今各个行业亟需解决的问题。随着人工智能技术的快速发展和普及&#xff0c;越来越多的企业开始将其应用于产品研发、销售流程优化、客…

一学就会---移除链表相同元素

文章目录 题目描述思路一&#xff1a;思路二&#xff1a; 题目描述 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例&#xff1a; 思路一&#xff1a; 要移除链表中值为val的结…

Lecture 3 N-gram Language Models

目录 Probabilities: Joint to Conditional 概率&#xff1a;联合概率到条件概率The Markov Assumption 马尔可夫假设Maximum Likelihood Estimation 最大似然估计Book-ending Sequences 序列的开头和结尾Problems with N-gram models N-gram模型的问题Smoothing 平滑处理In Pr…

Mujoco210 Ubuntu 22.04配置安装

.1 下载 1.1 解压 先是下载软件包 然后 mkdir ~/.mujoco缩包所在位置&#xff08;一般在下载目录下&#xff09;在终端打开&#xff0c;输入以下命令将压缩包解压到.mujoco文件夹中&#xff1a; tar -zxvf mujoco210-linux-x86_64.tar.gz -C ~/.mujoco1.2 许可问题 有说mu…

concrt140.dll丢失怎么修复?concrt140.dll丢失的最新修复教程

今天准备打开电脑软件时候&#xff0c;当打开我自己的软件后&#xff0c;弹出了一个对话框&#xff0c;内容是&#xff1a;由于找不到concrt140.dll&#xff0c;无法继续执行代码。重新安装程序可能会解决此问题。 我很纳闷&#xff0c;前几天还好好着呢。于是我上网上查了一下…

八、EGL实践

第一部分基础概念 1&#xff09;引入 之前的OpenGLes应用的开发都是使用类GLSurfaceView &#xff0c;然而GLSurfaceView 是内部已经实现了EGL的封装&#xff0c;也就是对Display&#xff0c;surface&#xff0c;context的管理。因此我们也很方便的利用GLSurfaceView.Rendere…

零基础入门网络安全/Web安全,收藏这一篇就够了

前言 由于我之前写了不少网络安全技术相关的文章和回答&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人私信问我&#xff1a; 我刚入门网络安全&#xff0c;该怎么学&#xff1f;要学哪些东西&#xff1f;有哪些方向&#xff1f;怎么选&…

高频面试八股文用法篇(八) == 和 equals 的区别

目录 区别 如何对equals重写 为何重写equals方法就得重写hashCode方法 扩展延伸 1、使用HashSet存储自定义类对象时为什么要重写equals和hashCode方法&#xff1f; 2、HashMap为什么要同时重写hashCode和equals方法 区别 一、对象类型不同 1、equals()&#xff1a;是超类…

第二章:MySQL环境搭建

第二章&#xff1a;MySQL环境搭建 2.1&#xff1a;MySQL的下载、安装、配置 MySQL的四大版本 MySQL Community Server社区版本&#xff1a;开源免费、自由下载&#xff0c;但不提供官方技术支持&#xff0c;适用于大多数普通用户。MySQL Enterprise Edition企业版本&#xff1…

SpringBoot个人博客系统(含源码+数据库)

一、作品设计理念 个人博客系统是一个让个人可以通过互联网自由表达、交流和分享的平台&#xff0c;是个人展示自己思想、感受和经验的品牌。设计理念对于任何一个个人博客系统来说都非常重要&#xff0c;它直接影响到用户的使用体验和网站的整体感觉。 好的设计理念应该着眼于…

小红书热搜榜TOP1,多巴胺时尚爆火,怎么抄作业?

今夏时尚&#xff0c;明媚与简约并存。要说今年夏天什么最火&#xff1f;多巴胺必须拥有姓名。无论男女、老少、人宠&#xff0c;都被这股快乐风带飞。 “多巴胺”有多火&#xff1f;就只是彩色穿搭吗&#xff1f;各大博主、品牌若想加入&#xff0c;要怎么玩&#xff1f;今儿&…

Python如何解决“京东滑块验证码”(5)

前言 本文是该专栏的第51篇,后面会持续分享python爬虫干货知识,记得关注。 多数情况下使用模拟登录会遇到滑块验证码的问题,对于普通的滑块验证码,使用selenium可以轻松解决。但是对于滑块缺失验证码,比如京东的滑块验证要怎么解决呢?京东滑块验证的这个滑块缺口,每次刷…

软件测试报告模板范文来了——优秀测试报告模板流程

一、软件测试报告是什么&#xff1f; 软件测试报告就是当软件开发人员开发出软件之后&#xff0c;在上市前交由测试人员进行一系列测试&#xff0c;再由测试人员对过程和结果的进行记录分析的一份文档。也是测试团队的工作成果展现&#xff0c;通过详细的记录测试内容&#xf…

算法修炼之筑基篇——筑基二层中期(讨论一下如何解决动态方程问题,没时间了,快快快看一下)

✨博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;算法修炼之练气篇 &#x1f353;专栏&#xff1a;算法修炼之筑基篇 ✨博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;学习了算法修炼之练气篇想必各位蒟蒻们的基础已经非常的扎实了&#xff0c;下来…

使用FFmpeg实现最简单的视频播放

按照之前的编译步骤我们会编译得到使用ffmpeg需要的文件&#xff0c;现在就使用ffmpeg实现最简单的视频播放 集成ffmpeg 使用Android Studio创建一个Native C项目编译之后得到三个文件夹 把include 文件夹放到cpp目录下面。 main 目录下面新建jniLibs 目录把lib文件下的so文件…

Java之BigDecimal使用

Java之BigDecimal使用 1、BigDecimal概述 ​ BigDecimal用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数&#xff0c;但在实际应用中&#xff0c;可能需要对更大或者更小的数进行运算和处理。一般情况下&#xff0c;对于那些不需要准确计…

OA系统,企业数字化转型的重要工具,用现成还是自己搭建呢

什么是OA系统 OA系统是办公自动化系统的简称&#xff0c;它是指一种基于计算机技术的办公工作管理系统&#xff0c;用于协调和规划企业内部各部门的信息发布、通信、人员流动、文档管理等方面的工作。它可以有效地提高企业办公效率和工作效益&#xff0c;优化企业内部沟通协作…

计算机视觉 | 深度学习预训练与MMPretrain

前言 MMPretrain是一款基于pytorch的开源深度学习预训练工具箱&#xff0c;是OenMMLab的项目成员之一。它是一个全新升级的预训练开源算法框架&#xff0c;旨在提供各种强大的预训练主干网络&#xff0c;并支持了不同的预训练策略。 一、MMPretrain算法库介绍 MMPretrain 源…

几分钟上线一个应用,这个神器我爱了!

配置一套公司企业运用的SaaS工作流办公管理系统需要多久&#xff1f;需要多少人才能开发出来&#xff1f;传统软件开发起码需要10个人&#xff0c;花上个把月时间&#xff0c;才能做出一套比较完整的SaaS工作流办公管理系统。 传统的开发模式它需要前后端程序员以及各平台系统的…

【Docker】浅谈Docker之AUFS、BTRFS、ZFS、Container、分层的概念

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…