pytest使用简介
安装
pip install pytest
简介
pytest
可以轻松编写测试,支持扩展,并且有丰富的引用和库支持复杂的功能测试
一个简单的例子:
# content of test_sample.py def inc(x): return x + 1 def test_answer(): assert inc(3) == 5
执行结果
$ pytest =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item test_sample.py F [100%] ================================= FAILURES ================================= _______________________________ test_answer ________________________________ def test_answer(): > assert inc(3) == 5 E assert 4 == 5 E + where 4 = inc(3) test_sample.py:6: AssertionError ========================= 1 failed in 0.12 seconds =========================
功能特点
- 失败的语句有详尽的信息 (无需记住 self.assert* names);
- 自动发现测试的模块和方法(通过模块和功能的命名方式);
- 使用fixtures用于管理测试资源;
- 可以兼容 unittest和nose测试组件;
- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (未经测试);
- 丰富的插件资源, 超过315个外部插件和蓬勃发展的社区;
- 支持参数化
- 执行测试过程中可以将某些测试跳过,或者对某些预期失败的case标记成失败
- 支持重复执行失败的case
fixtures
fixtures提供一个固定的基线,可以可靠地重复执行测试。pytest fixture比经典的xUnit的setup/teardown 功能提供了显着的改进:
- fixtures具有明确的名称,并通过从测试功能,模块,类或整个项目中声明它们的使用来激活。
- fixtures以模块化方式实现,因为每个fixtures名称触发fixtures方法,该fixtures方法本身可以使用其他fixtures。
- fixtures管理从简单的单元扩展到复杂的功能测试,允许根据配置和组件选项对fixtures和测试进行参数化,或者在功能,类,模块或整个测试会话范围内重复使用fixtures。
此外,pytes也支持经典的 xunit风格 。您可以根据需要混合使用两种样式,逐步从经典样式移动到新样式。您也可以从现有的unittest.TestCase样式或基于nose的项目开始。
xunit风格:
- setUp/tearDown;
setUpClass/tearDownClass
;setUpModule/tearDownModule
;
拓展作用域:
- 模块级(setup_module/teardown_module)开始于模块始末,全局的
- 函数级(setup_function/teardown_function)只对函数用例生效(不在类中)
- 类级(setup_class/teardown_class)只在类中前后运行一次(在类中)
- 方法级(setup_method/teardown_method)开始于方法始末(在类中)
- 类里面的(setup/teardown)运行在调用方法的前后
fixtures作为函数参数
让我们看一个简单的独立测试模块,它包含一个fixture和一个使用它的测试函数
# content of ./test_smtpsimple.py 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 assert 0 # for demo purposes
在这里,test_ehlo
需要smtp_connectio的返回值。pytest发现并调用
@pytest.fixture 标记的
smtp_connection fixture函数。运行测试如下所示:
$ pytest test_smtpsimple.py =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item test_smtpsimple.py F [100%] ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ smtp_connection = <smtplib.SMTP object at 0xdeadbeef> def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 > assert 0 # for demo purposes E assert 0 test_smtpsimple.py:11: AssertionError ========================= 1 failed in 0.12 seconds =========================
pytest 调用执行过程如下:
- pytest 发现了test_ehlo函数,因为以test_前缀。test_ehlo需要一个名为smtp_connection的函数参数。通过查找名为标记fixture的函数来发现匹配smtp_connection函数
- smtp_connection() 被调用来创建一个实例
- test_ehlo(<smtp_connection instance>) 被调用
共享fixture功能
如果在实施测试期间您意识到要使用多个测试文件中的fixture功能,则可以将其移动到conftest.py文件中。您不需要导入要在测试中使用的夹具,它会自动被pytest发现。
下面这个示例将fixture函数放入单独的conftest.py
文件中,以便来自目录中多个测试模块的测试可以访问fixture函数
# content of conftest.py import pytest import smtplib @pytest.fixture(scope="module") def smtp_connection(): return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
这个fixture的名字是smtp_connection,可以在任何测试文件(conftest.py所在目录中下的)将名称列为输入参数来访问:
# content of test_module.py def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 assert b"smtp.gmail.com" in msg assert 0 # for demo purposes def test_noop(smtp_connection): response, msg = smtp_connection.noop() assert response == 250 assert 0 # for demo purposes
fixture作用域
@pytest.fixture(scope="module")使用scope控制fixture的作用域,function,class,module,package,session。
变量名称 | 作用范围 | xunit风格对比 |
---|---|---|
function | 开始于方法始末(在类中)默认 | setup_method |
class | 只在类中前后运行一次,每一个类调用一次,一个类可以有多个方法 | setup_class |
module | 开始于模块始末,全局的,每一个.py文件调用一次,该文件内又有多个function和class | setup_module |
session | 是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module |
fixture的teardown 代码
用xunit风格的的代码setup和teardown都是成对出现的,pytest除了兼容这种模式外,pytest还支持fixture特定的终结代码的执行。通过使用yield
语句而不是return
,yield语句之后的所有代码都用作teardown代码:
# content of conftest.py 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()
当模块中的最后一次测试已经完成,无论测试的情况如何的语句smtp_connection.close()将被执行
让我们执行:
$ pytest -s -q --tb=no FFteardown smtp 2 failed in 0.12 seconds
请注意,如果我们使用
scope='function'
夹具设置fixture修饰的方法,则每次单独测试都会进行清理
我们也可以使用yield
语法with
的语句
# content of test_yield2.py import smtplib import pytest @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
yield关键字是在python语法生成器使用,用来节省内存
参数化
fixture参数化
当fixture方法被多次调用,并且每次执行一组相同的测试,在这种情况下,可以对fixture方法进行参数化。
扩展前面的示例,我们可以通过标记fixture的方法创建两个 smtp_connection 实例。fixture函数通过request对象访问每个参数:
# content of conftest.py import pytest import smtplib @pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"]) def smtp_connection(request): smtp_connection = smtplib.SMTP(request.param, 587, timeout=5) yield smtp_connection print("finalizing %s" % smtp_connection) smtp_connection.close()
运行测试:
$ pytest -q test_module.py FFFF [100%] ================================= FAILURES ================================= ________________________ test_ehlo[smtp.gmail.com] _________________________ smtp_connection = <smtplib.SMTP object at 0xdeadbeef> def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 assert b"smtp.gmail.com" in msg > assert 0 # for demo purposes E assert 0 test_module.py:6: AssertionError ________________________ test_noop[smtp.gmail.com] _________________________ smtp_connection = <smtplib.SMTP object at 0xdeadbeef> def test_noop(smtp_connection): response, msg = smtp_connection.noop() assert response == 250 > assert 0 # for demo purposes E assert 0 test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ smtp_connection = <smtplib.SMTP object at 0xdeadbeef> def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 > assert b"smtp.gmail.com" in msg E AssertionError: assert b'smtp.gmail.com' in b'mail.python.org\nPIPELINING\nSIZE 51200000\nETRN\nSTARTTLS\nAUTH DIGEST-MD5 NTLM CRAM-MD5\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8\nCHUNKING' test_module.py:5: AssertionError -------------------------- Captured stdout setup --------------------------- finalizing <smtplib.SMTP object at 0xdeadbeef> ________________________ test_noop[mail.python.org] ________________________ smtp_connection = <smtplib.SMTP object at 0xdeadbeef> def test_noop(smtp_connection): response, msg = smtp_connection.noop() assert response == 250 > assert 0 # for demo purposes E assert 0 test_module.py:11: AssertionError ------------------------- Captured stdout teardown ------------------------- finalizing <smtplib.SMTP object at 0xdeadbeef> 4 failed in 0.12 seconds
我们看到两个测试函数分别针对不同的smtp_connection实例运行了两次
测试函数参数
内置的pytest.mark.parametrize装饰器支持测试函数的参数的参数化。以下是测试函数的典型示例,该函数实现检查某个输入是否导致预期输出:
# content of test_expectation.py import pytest @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
次使用它们运行三次:
$ pytest =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 3 items test_expectation.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_expectation.py:6: AssertionError ==================== 1 failed, 2 passed in 0.12 seconds ====================
调用fixture的方式
# content of conftest.py import pytest import smtplib @pytest.fixture(scope="module") def smtp_connection(): return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
在方法中可以直接使用fixture标识的函数名调用:
# content of test_module.py def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 assert b"smtp.gmail.com" in msg assert 0 # for demo purposes
也可以用声明装饰器@pytest.mark.usefixtures调用
# content of test_module.py @pytest.mark.usefixtures("smtp_connection") def test_ehlo(): response, msg = smtp_connection.ehlo() assert response == 250 assert b"smtp.gmail.com" in msg assert 0 # for demo purposes
当装饰器@pytest.mark.usefixtures作用于类的时,如果这个@pytest.fixture的scope=function,那么类中的每个测试方法都会调用这个fixture。
@pytest.fixture(scope="module", autouse=True),参数autouse, 默认设置为False。 当默认为False,就可以选择用上面两种方式来试用fixture。 当设置为True时,在一个session内的所有的test都会自动调用这个fixture。 所以用该功能时也要谨慎小心
pytest常用插件
pip install pytest-html #轻量级的测试报告 pytest '文件' --html=report.html pip install pytest-sugar # 打印进度 pip install pytest-rerunfailures # 失败重试 pip install pytest-ordering # 执行顺序 pip install pytest-allure-adaptor #测试报告的升级版,功能完备,界面酷炫
更多内容欢迎大家访问我的个人博客 http://www.hongmenliyu.com/article/2019/5/6/101.html

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
大话文本识别经典模型:CRNN
在前一篇文章中(详见本博客文章:大话文本检测经典模型 CTPN),介绍了文字识别在现实生活中的广泛应用,以及文字识别的简单流程: 其中“文本检测”、“文本识别”是其中两个关键环节,“文本检测”已经在前一篇文章中介绍了详细的介绍,本文主要介绍“文本识别”的经典模型CRNN及其原理。 在介绍CRNN之前,先来梳理一下要实现“文本识别”的模型,需要具备哪些要素: (1)首先是要读取输入的图像,提取图像特征,因此,需要有个卷积层用于读取图像和提取特征。具体原理可详见本公众号的文章:白话卷积神经网络(CNN); (2)由于文本序列是不定长的,因此在模型中需要引入RNN(循环神经网络),一般是使用双向LSTM来处理不定长序列预测的问题。具体原理可详见本公众号的文章:白话循环神经网络(RNN); (3)为了提升模型的适用性,最好不要要求对输入字符进行分割,直接可进行端到端的训练,这样可减少大量的分割标注工作,这时就要引入CTC模型(Connectionist temporal classification, 联接时间分类),来解决样本的分割对齐的问题。 (4)最后根据一定的规则,对模型输出结果进行...
- 下一篇
马蜂窝 iOS App 启动治理:回归用户体验
增长、活跃、留存是移动 App 的常见核心指标,直接反映一款 App 甚至一个互联网公司运行的健康程度和发展动能。启动流程的体验决定了用户的第一印象,在一定程度上影响了用户活跃度和留存率。因此,确保启动流程的良好体验至关重要。 「马蜂窝旅游」App 是马蜂窝为用户提供服务的主要阵地,其承载的业务模块不断丰富和完善,产品功能日趋复杂,已经逐渐成长为一个集合旅行信息、出行决策、自由行产品及服务交易的一站式移动平台。 「马蜂窝旅游」iOS App 历经几十个版本的开发迭代,在启动流程上积累了一定的技术债务。为了带给用户更流畅的使用体验,我们团队实施了数月的专项治理,也总结出一些 iOS 启动治理方面的实践经验,借由本文和大家分享。 0X0如何定义「启动」 要分析和解决启动问题,我们首先需要界定启动的内涵和边界,从哪开始、到哪结束,中间经历了哪些阶段和过程。以不同视角去观察时,可以得出不同结论。 技术视角 App 启动原本就是程序启动的技术过程。作为开发人员,我们很自然地更愿意从技术阶段去看待和定义启动的流程。 App 启动的方式分为冷启动和热启动两种。简单来说,冷启动发生时后台是没有这个应...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装Docker,最新的服务器搭配容器使用
- Linux系统CentOS6、CentOS7手动修改IP地址
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Hadoop3单机部署,实现最简伪集群
- CentOS8编译安装MySQL8.0.19
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池