当前位置: 首页 > 后端技术 > Python

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

时间:2023-03-26 13:20:52 Python

作者:HelloGitHub-Prodesire本文涉及的示例代码已更新至HelloGitHub-Team仓库。1.简介本文是《聊聊 Python 的单元测试框架》的第三篇,前两篇分别介绍了标准库unittest和第三方单元测试框架nose。作为本系列的最后一篇,压轴的是Python界最火的第三方单元测试框架:pytest。pytest项目地址:https://github.com/pytest-dev...主要有以下特点:assert断言失败时输出详细信息(不用再记住self.assert*的名字)自动发现测试模块和功能模块化夹具用于管理各种测试资源。完全兼容unittest,基本兼容nose。有超过315个第三方插件。社区繁荣与前面介绍的unittest和nose一样。我们将从以下几个方面来讨论介绍pytest的特性。2、用例编写和nose一样,pytest支持函数和测试类形式的测试用例。最大的不同是,你可以使用assert语句进行断言,而不必担心它会在nose或unittest中生成的详细上下文信息的缺失。例如,在下面的测试示例中,test_upper中的断言是故意失败的:importpytestdeftest_upper():assert'foo'.upper()=='FOO1'classTestClass:deftest_one(self):x="this"assert"h"inxdeftest_two(self):x="hello"withpytest.raises(TypeError):x+[]当使用pytest执行测试用例时,它会输出详细的(和多色的)上下文信息:=====================================测试会话开始======================================平台——Python3.7.1、pytest-4.0.1、py-1.7.0,pluggy-0.8.0rootdir:/Users/prodesire/projects/tests,inifile:plugins:cov-2.6.0collected3itemstest.pyF..[100%]==========================================失败============================================______________________________________test_upper___________________________________deftest_upper():>assert'foo'.upper()=='FOO1'EAssertionError:assert'FOO'=='FOO1'E-FOOE+FOO1E?+test.py:4:AssertionError==============================1个失败,2个在0.08秒内通过==============================不难看出pytest不仅输出了测试代码上下文,还输出了被测的信息变量值相比nose和unittest,pytest允许用户以更简单的方式编写测试用例,并获得更丰富和友好的测试结果。3.用例发现和执行pytest支持unittest和nose支持的用例发现和执行能力。pytest支持自动(递归)发现测试用例:默认会发现当前目录下所有符合test_*.py或*_test.py的测试用例文件,以test开头的测试函数或以test开头的测试类测试方法使用pytest命令,与nose2的概念相同。通过在配置文件中指定具体的参数,可以配置用例文件、类和函数的名称模式(模糊匹配)。pytest也支持指定用例的执行:指定测试文件路径pytest/path/to/test/file.py指定测试类pytest/path/to/test/file.py:TestCase指定测试方法pytestanother.test::TestClass::test_method指定测试函数pytest/path/to/test/file.py:test_function4.测试夹具(Fixtures)pytest的测试夹具在风格上与unittest、nose、nose2有很大区别。它不仅可以实现setUp、tearDown等测试预测试和清理逻辑,还有很多其他强大的功能。4.1声明和使用测试夹具在pytest中更像是测试资源,你只需要定义一个夹具,然后就可以直接在测试用例中使用了。得益于pytest的依赖注入机制,不需要以fromxximportxx的形式显示导入,只需要在测试函数的参数中指定同名参数即可,例如:importpytest@pytest.fixturedefsmtp_connection():importsmtplibreturnsmtplib.SMTP("smtp.gmail.com",587,timeout=5)deftest_ehlo(smtp_connection):response,msg=smtp_connection.ehlo()断言响应==250上面的例子定义了一个测试fixturesmtp_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不能。`--测试_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——session级别,在运行所有用例之前,当我们指定有效级别为模块级别时,fixture只会生成一次,示例如下:importpytestimportsmtplib@pytest.fixture(scope="module")defsmtp_connection():returnsmtplib.SMTP("smtp.gmail.com",587,timeout=5)4.4测试前置和清理pytest的testfixture也可以实现测试prepending和cleanup。通过yield语句拆分两个逻辑,写法变得很简单,如:com",587,timeout=5)yieldsmtp_connection#providethefixturevalueprint("teardownsmtp")smtp_connection.close()上面例子中yieldsmtp_connection和前面的语句相当于测试前测试,返回通过yield准备测试资源smtp_connection;而后面的语句会在用例执行结束后(准确的说是testfixture有效层的语句期结束)执行,相当于testcleanup。如果生成测试资源的过程(如例子中的smtp_connection)支持with语句,也可以写成更简单的形式:@pytest.fixture(scope="module")defsmtp_connection():withsmtplib.SMTP("smtp.gmail.com",587,timeout=5)assmtp_connection:yieldsmtp_connection#提供fixture值pytest的testfixture除了文中介绍的功能外,还有参数化fixture、factoryfixture、使用的fixture等在fixtures等。高级玩法请阅读《pytestfixtures:explicit,modular,scalable》了解详情。5.跳过测试和预测失败pytest除了支持unittest和nosetest跳过测试和预测失败外,pytest在pytest.mark中也提供了相应的方法:直接通过skip装饰器跳过测试或者pytest.skip函数通过skipif根据条件跳过测试并通过xfail。预期的测试失败示例如下:@pytest.mark.skip(reason="nowayofcurrentlytestingthis")deftest_mark_skip():...deftest_skip():ifnotvalid_config():pytest.skip(“不支持的配置”)@pytest.mark.skipif(sys.version_info<(3,6),reason="requirespython3.6orhigher")deftest_mark_skip_if():...@pytest.mark。xfaildeftest_mark_xfail():...更多跳过测试和预测失败的方法,参见《Skipandxfail:dealingwithteststhatcannotsucceed》6.子测试/参数化测试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)])deftest_eval(test_input,expected):asserteval(test_input)==expected在这个例子中,最后一组是故意做参数导致失败,运行用例可以看到丰富的测试结果输出:============================================测试会话开始=============================================平台darwin——Python3.7.1、pytest-4.0.1、py-1.7.0、pluggy-0.8.0rootdir:/Users/prodesire/projects/tests,inifile:plugins:cov-2.6.0collected3itemstest.py。.F[100%]================================================失败===================================================__________________________________________test_eval[6*9-42]__________________________________________test_input='6*9',预期=42@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+4",6),("6*9",42)])deftest_eval(test_input,expected):>asserteval(test_input)==expectedEAssertionError:assert54==42E+where54=eval('6*9')test.py:6:断言错误===================================1个失败,2个在0.09秒内通过====================================如果把参数换成pytest.param,我们也可以有更高层次的玩法,比如知道最后参数集失败,因此将其标记为xfail:@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+4",6),pytest.param("6*9",42,marks=pytest.mark.xfail)],)deftest_eval(test_input,expected):asserteval(test_input)==expected如果测试函数的多个参数的值要排列并相互结合,我们可以这样写:@pytest.mark.parametrize("x",[0,1])@pytest.mark.parametrize("y",[2,3])deftest_foo(x,y):pass在上面的例子中,将x=0/y=2,x=1/y=2,x=0/y=3,x=1/y=3带入测试函数,并且他们被认为是四吨估计要执行的情况。7、测试结果输出pytesttest相比于unittest和nose,结果输出更加丰富。其优点是:高亮输出,通过或失败会用不同颜色区分更丰富的上下文信息,自动输出代码上下文和变量信息测试进度显示测试结果的输出排版更友好易读。8、插件系统pytest的插件非常丰富,即插即用,用户不需要额外编写代码。插件的使用参见《插件的安装与使用》。另外,得益于pytest良好的架构设计和hook机制,其插件化编写也变得简单易用。有关编写插件的信息,请参阅“编写插件”。九。总结Python测试框架的三篇介绍到此结束。写了这么多,估计评委都看腻了。我们不妨列一个横向对比表来总结一下这几个单元测试框架的异同点:testfixture?????测试fixture的种类预清洗预清洗预清洗预清洗预清洗预清洗内置各种fixture自定义各种fixture测试fixture有效levelmethod,class,modulemethod,class,modulemethod,class,modulemethod,classes,modules,packages,session支持跳过测试和预测失败????Subtests????测试结果输出普遍越来越好Plug-in越来越丰富,hook普遍丰富--??作为标准库的社区生态,从官方维护,低活跃,高活跃Python单元测试框架看似五花八门,其实是代代相传的基因进化口粮,有痕迹可循。把握它的特点,结合使用场景,就可以轻松做出选择。如果你不想安装或允许第三方库,那么unittest是最好的也是唯一的选择。相反,pytest无疑是最好的选择。许多Python开源项目(比如著名的requests)都使用pytest作为单元测试框架。连nose2在官方文档中都推荐你使用pytest,佩服!《讲解开源项目系列》——让对开源项目感兴趣的人不再害怕,让开源项目的发起者不再孤单。关注我们的文章,您将发现编程的乐趣,使用并发现参与开源项目是多么容易。欢迎留言联系我们,加入我们,让更多人爱上开源,为开源做贡献^@^