批量发布网站,网站调用视频,wordpress技术网主题,深圳知名装修设计公司作者#xff1a;HelloGitHub-Prodesire一、介绍本篇文章是《聊聊 Python 的单元测试框架》的第三篇#xff0c;前两篇分别介绍了标准库 unittest 和第三方单元测试框架 nose。作为本系列的最后一篇#xff0c;压轴出场的是Python 世界中最火的第三方单元测试框架#xff1a…作者HelloGitHub-Prodesire一、介绍本篇文章是《聊聊 Python 的单元测试框架》的第三篇前两篇分别介绍了标准库 unittest 和第三方单元测试框架 nose。作为本系列的最后一篇压轴出场的是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() FOO1class TestClass:def test_one(self):x thisassert h in xdef test_two(self):x hellowith 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() FOO1E AssertionError: assert FOO FOO1E - FOOE FOO1E ? test.py:4: AssertionError 1 failed, 2 passed in 0.08 seconds 不难看到pytest 既输出了测试代码上下文也输出了被测变量值的信息。相比于 nose 和 unittestpytest 允许用户使用更简单的方式编写测试用例又能得到一个更丰富和友好的测试结果。三、用例发现和执行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 pytestpytest.fixturedef smtp_connection():import smtplibreturn smtplib.SMTP(smtp.gmail.com, 587, timeout5)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.py4.3 生效级别unittest 和 nose 均支持测试前置和清理的生效级别测试方法、测试类和测试模块。pytest 的测试夹具同样支持各类生效级别且更加丰富。通过在 pytest.fixture 中指定 scope 参数来设置function —— 函数级即调用每个测试函数前均会重新生成 fixtureclass —— 类级调用每个测试类前均会重新生成 fixturemodule —— 模块级载入每个测试模块前均会重新生成 fixturepackage —— 包级载入每个包前均会重新生成 fixturesession —— 会话级运行所有用例前只生成一次 fixture当我们指定生效级别为模块级时示例如下import pytestimport smtplibpytest.fixture(scopemodule)def smtp_connection():return smtplib.SMTP(smtp.gmail.com, 587, timeout5)4.4 测试前置和清理pytest 的测试夹具也能够实现测试前置和清理通过 yield 语句来拆分这两个逻辑写法变得很简单如import smtplibimport pytestpytest.fixture(scopemodule)def smtp_connection():smtp_connection smtplib.SMTP(smtp.gmail.com, 587, timeout5)yield smtp_connection # provide the fixture valueprint(teardown smtp)smtp_connection.close()在上述示例中yield smtp_connection 及前面的语句相当于测试前置通过 yield 返回准备好的测试资源 smtp_connection;而后面的语句则会在用例执行结束(确切的说是测试夹具的生效级别的声明周期结束时)后执行相当于测试清理。如果生成测试资源(如示例中的 smtp_connection)的过程支持 with 语句那么还可以写成更加简单的形式pytest.fixture(scopemodule)def smtp_connection():with smtplib.SMTP(smtp.gmail.com, 587, timeout5) as smtp_connection:yield smtp_connection # provide the fixture value五、跳过测试和预计失败pytest 除了支持 unittest 和 nosetest 的跳过测试和预计失败的方式外还在 pytest.mark 中提供对应方法通过 skipif按条件跳过测试通过 xfail 预计测试失败示例如下pytest.mark.skip(reasonno 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), reasonrequires 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, [(35, 8), (24, 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 42pytest.mark.parametrize(test_input,expected, [(35, 8), (24, 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我们还可以有更高阶的玩法比如知道最后一组参数是失败的所以将它标记为 xfailpytest.mark.parametrize(test_input,expected,[(35, 8), (24, 6), pytest.param(6*9, 42, markspytest.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上述示例中会分别把 x0/y2、x1/y2、x0/y3和x1/y3带入测试函数视作四个测试用例来执行。七、测试结果输出pytest 的测试结果输出相比于 unittest 和 nose 来说更为丰富其优势在于高亮输出通过或不通过会用不同的颜色进行区分更丰富的上下文信息自动输出代码上下文和变量信息测试进度展示测试结果输出布局更加友好易读八、插件体系pytest 的插件十分丰富而且即插即用作为使用者不需要编写额外代码。关于插件的使用参见Installing and Using plugins。此外得益于 pytest 良好的架构设计和钩子机制其插件编写也变得容易上手。关于插件的编写参见Writing plugins。九、总结三篇关于 Python 测试框架的介绍到这里就要收尾了。写了这么多各位看官怕也是看得累了。我们不妨罗列一个横向对比表来总结下这些单元测试框架的异同unittestnosenose2pytest自动发现用例✔✔✔✔指定(各级别)用例执行✔✔✔✔支持 assert 断言弱弱弱强测试夹具✔✔✔✔测试夹具种类前置和清理前置和清理前置和清理前置、清理、内置各类 fixtures自定义各类 fixtures测试夹具生效级别方法、类、模块方法、类、模块方法、类、模块方法、类、模块、包、会话支持跳过测试和预计失败✔✔✔✔子测试✔✔✔✔测试结果输出一般较好较好好插件-较丰富一般丰富钩子--✔✔社区生态作为标准库由官方维护停止维护维护中活跃度低维护中活跃度高Python 的单元测试框架看似种类繁多实则是一代代的进化有迹可循。抓住其特点结合使用场景就能容易的做出选择。若你不想安装或不允许第三方库那么 unittest 是最好也是唯一的选择。反之pytest 无疑是最佳选择众多 Python 开源项目(如大名鼎鼎的 requests)都是使用 pytest 作为单元测试框架。甚至连 nose2 在官方文档上都建议大家使用 pytest这得是多大的敬佩呀『讲解开源项目系列』——让对开源项目感兴趣的人不再畏惧、让开源项目的发起者不再孤单。跟着我们的文章你会发现编程的乐趣、使用和发现参与开源项目如此简单。欢迎留言联系我们、加入我们让更多人爱上开源、贡献开源