pytest框架_Python最火的第三方开源测试框架——pytest

一、介绍

本篇文章是介绍的是Python 世界中最火的第三方单元测试框架:pytest。它有如下主要特性:

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

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

二、用例编写

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

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

import pytestdef 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.0rootdir: /Users/prodesire/projects/tests, inifile:plugins: cov-2.6.0collected 3 itemstest.py F..                                                                         [100%]======================================== FAILURES =========================================_______________________________________ test_upper ________________________________________    def test_upper():>       assert 'foo'.upper() == 'FOO1'E       AssertionError: assert 'FOO' == 'FOO1'E         - FOOE         + FOO1E         ?    +test.py:4: AssertionError=========================== 1 failed, 2 passed in 0.08 seconds ============================

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

三、用例发现和执行

unittest 和 nose 所支持的用例发现和执行能力,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 的测试夹具和 unittest、nose、nose2的风格迥异,它不但能实现 setUp 和 tearDown这种测试前置和清理逻辑,还其他非常多强大的功能。

4.1 声明和使用

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

import pytest@pytest.fixturedef 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.py 和 test_b.py 可以使用该测试夹具;而 test_c.py 则无法使用。

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

4.3 生效级别

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

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

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

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

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

4.4 测试前置和清理

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

import smtplibimport 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 除了支持 unittest 和 nosetest 的跳过测试和预计失败的方式外,还在 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.xfaildef test_mark_xfail():    ...

六、子测试/参数化测试

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.0rootdir: /Users/prodesire/projects/tests, inifile:plugins: cov-2.6.0collected 3 itemstest.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) == expectedE       AssertionError: assert 54 == 42E        +  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=2、x=1/y=2、x=0/y=3和x=1/y=3带入测试函数,视作四个测试用例来执行。

七、测试结果输出

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

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

八、插件体系

pytest 的插件十分丰富,而且即插即用,作为使用者不需要编写额外代码。

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

九、总结

三篇关于 Python 测试框架的介绍到这里就要收尾了。写了这么多,各位看官怕也是看得累了。我们不妨罗列一个横向对比表,来总结下这些单元测试框架的异同:

30f89bcbafff6e249af3fd04f9554a9b.png

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

若你不想安装或不允许第三方库,那么 unittest 是最好也是唯一的选择。反之,pytest 无疑是最佳选择,众多 Python 开源项目都是使用 pytest 作为单元测试框架。

以上内容就是本篇的全部内容以上内容希望对你有帮助。

如果对接口、性能、自动化测试、面试经验交流等感兴趣的,可以关注我的头条号,我会不定期的发放免费的资料,这些资料都是从各个技术网站搜集、整理出来的,如果你有好的学习资料可以私聊发我,我会注明出处之后分享给大家。欢迎分享,欢迎评论,欢迎转发。需要资料的同学可以关注小编+转发文章+私信【测试资料】

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

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

相关文章

jvm jstat_使用jstat的JVM统计信息

jvm jstat过去&#xff0c;我已经写过关于Oracle和/或OpenJDK Java开发工具包&#xff08;JDK&#xff09;附带的几个命令行工具的信息&#xff0c;但是我从来没有专门写过jstat工具 。 Oracle JDK 9文档Early Access指出jstat用于“监视Java虚拟机&#xff08;JVM&#xff09;…

win10录屏工具_win10怎么录屏?这才是最长情陪伴你的录屏工具

win10怎么录屏&#xff1f;现在看视频的用户是越来越多了&#xff0c;一些经验类的视频教程越来越多&#xff0c;不仅能够教我们学习技能&#xff0c;也能给我们看一些会员才能看的视频内容&#xff0c;他们怎么怎么做的呢&#xff1f;当然是电脑录屏了&#xff0c;由于现在的电…

python多行注释符号_涨知识Python 为什么用 # 号作注释符?

- START -关于编程语言中的注释&#xff0c;其重要性基本上已为大家所共识。然而关于注释的规范&#xff0c;这个话题就像我们之前聊过的缩进、终止符和命名方式一样&#xff0c;众口难调。注释符通常可分为两种&#xff0c;即行注释与块注释(inline/block)&#xff0c;它们在不…

Sublime Text如何安装和卸载插件

文章目录如何查找插件如何安装插件通过包控制器安装插件手动安装插件如何卸载插件如何查找插件 建议先通过插件库来搜索你想要的插件&#xff0c;找到你想要的插件之后&#xff0c;你再选择安装方式进行安装&#xff0c;搜索插件请点击以下链接&#xff1a; Package Control中…

@insert 对象_python中列表插入append(), extend(), insert()

1 append()append&#xff1a;只能接收一个参数&#xff0c;并且只能添加在列表的最后。添加数字 In [1]: a [1,2,3] In [2]: a.append(4) In [3]: a Out[3]: [1, 2, 3, 4] 添加字符串 In [6]: a [1,2,3] In [7]: a.append("daniel") In [8]: a Out[8]: [1, 2, 3, …

常用jdk的命令行工具:_jhsdb:JDK 9的新工具

常用jdk的命令行工具:我喜欢在分析性能和基于Java的应用程序的其他问题的早期步骤中使用JDK随附的命令行工具&#xff0c;并在诸如jcmd &#xff0c; jps &#xff0c; jstat &#xff0c; jinfo &#xff0c; jhat和jmap &#xff0c; jrunscript &#xff0c; jstack之类的 工…

佳能2900打印机与win10不兼容_佳能2900打印机和惠普1020哪种好 佳能2900打印机和惠普1020对比【详解】...

现在的人们早已经习惯遇到身边美丽的景色&#xff0c;就用手机进行拍摄下来了&#xff0c;若想长久的留住这一刻的美好&#xff0c;拥有一台 打印机 就是很不错的选择&#xff0c;如今打印机的需求量在不断的增加&#xff0c;市面上的打印机品牌也在不断的增多&#xff0c;我们…

mysql 多表查询 join on_mysql多表查询

在做连表查询的时候&#xff0c;可以在联合的字段上面&#xff0c;分别加上索引字段&#xff0c;这样有加快搜索的速度左右连表查询时条件放在on后面和where后面的执行时机是不一样的例如test1表test2表执行SELECT * FROM test1 t1 LEFT JOIN test2 t2ON t1.not2.no AND t2.nam…

redis key失效的事件_《分享几道高频 Redis 高频面试题,面试不用愁》

1、说说 Redis 都有哪些应用场景&#xff1f;缓存&#xff1a;这应该是 Redis 最主要的功能了&#xff0c;也是大型网站必备机制&#xff0c;合理地使用缓存不仅可以加 快数据的访问速度&#xff0c;而且能够有效地降低后端数据源的压力。共享Session&#xff1a;对于一些依赖 …

解决文件内容的中文乱码_字符集_字符编码_字符编码方案

从第三方下载的java源文件&#xff0c;打开查看里面的中文全部是乱码&#xff0c;无论你使用什么字符编码集都无法正常显示&#xff0c;该文件是用UTF-8编码存档的&#xff0c;使用UTF-8解码也同样是乱码&#xff0c;相信很多人遇到类似的问题&#xff0c;我这里解决过一个经典…

rust为什么显示不了国服_捋捋 Rust 中的 impl Trait 和 dyn Trait

缘起一切都要从年末换工作碰上特殊时期, 在家闲着无聊又读了几首诗, 突然想写一个可以浏览和背诵诗词的 TUI 程序说起. 我选择了 Cursive 这个 Rust TUI 库. 在实现时有这么一个函数, 它会根据参数的不同返回某个组件(如 Button, TextView 等). 在 Cursive 中, 每个组件都实现了…

mysql不支持子查询_MySQL不支持子查询优化一例

一创建表 create table tt1(id int primary key, c1 INT);create table tt2(id int primary key, c2 INT);insert into tt1 value一创建表create table tt1(id int primary key, c1 INT);create table tt2(id int primary key, c2 INT);insert into tt1 values(1,1),(2,2),(3,3…

Mysql索引使用情况_介绍mysql索引失效的情况

mysql视频教程栏目索引失效的情况。索引对于MySQL而言&#xff0c;是非常重要的篇章。索引知识点也巨多&#xff0c;要想掌握透彻&#xff0c;需要逐个知识点一一击破&#xff0c;今天来先来聊聊哪些情况下会导致索引失效。图片总结版相关免费学习推荐&#xff1a;mysql视频教程…

java 类 null_深入理解java中的null“类型”

本文研究的主要是java中的null“类型”的相关实例&#xff0c;具体介绍如下。先给出一道简单的null相关的题目&#xff0c;引发我们对null的探讨,后面会根据官方语言手册对null“类型”进行解读。题目&#xff1a;下面程序能正确运行吗&#xff1f;解析&#xff1a;输出应该为 …

如何彻底禁用Chrome浏览器的缓存功能

禁止Chrome浏览器的缓存功能有几种方式&#xff1a; 使用Shift F5强制刷新缓存。实际使用中我们发现&#xff0c;这种方法对于部分网页的更改并不能做到释放缓存的作用&#xff0c;尤其是无法释放DNS缓存。 使用隐身模式Shift Control N. 这种方法只能在打开的页面上消除之…

java int integer内存_java中一个integer对象的内存占用是多少?可以通过java方法输出吗?...

今天翻记录看到了自己15年3月份提的问题&#xff0c;现在已经18年6月份了&#xff1b;去年线上项目出现内存瓶颈&#xff0c;原因是缓存的玩家角色数据过多&#xff0c;在长时间不停服的情况下&#xff0c;导致数据越来越膨胀&#xff0c;之前没有清除无用缓存的机制&#xff0…

windows查看器无法打开图片_关于windows微软商城无法打开 错误代码0x80004003问题...

我本来是商城可以进 但是一打开里面的东西就无法打开 按下面的刷新就闪退 如下图操作桌面winr 运行 services.msc然后找到windows update打开就行 但是开启了系统会自动更新若是修改时显示拒绝访问桌面winr 输入regedit打开注册表 注册表找到HKEY_LOCAL_MACHINESYSTEMCurrentCo…

win10 java 反编译_win10下对.java源文件进行编译和反编译的步骤

1、先写好代码&#xff0c;如下找了个示例代码&#xff0c;文件名是Mutex.java&#xff1a;package com.huangshiyi.concurrent.cha04;import java.util.concurrent.TimeUnit;public class Mutex {private final static Object MUTEX new Object();public void accessResource…

电容屏物体识别_相比传统的触摸屏,电容式触摸屏的优势

点击蓝字&#xff0c;轻松关注今日话题 如今&#xff0c;我们的身边有着许多电子产品&#xff0c;主要是因为它便于我们的生活&#xff0c;所以我们的身边出现了许多触摸屏。对于电子产品&#xff0c;我们使用之前要了解清楚它的使用性能&#xff0c;掌握多点信息&#xff0c;这…

java底层机制_Java同步机制的底层实现

在多线程编程中我们会遇到很多需要使用线程同步机制去解决的并发问题&#xff0c;而这些同步机制就是多线程编程中影响正确性和运行效率的重中之重。这不禁让我感到好奇&#xff0c;这些同步机制是如何实现的呢&#xff1f;好奇心是进步的源泉&#xff0c;就让我们一起来揭开同…