企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] >[info] 学习了一段时间的python入门基础了,大家应该已经具备开发接口测试代码的能力了。进行接口测试,我们还需要掌握一种测试框架,来组织我们的用例执行,用例断言。下面我们介绍一款最666的测试框架`pytest` ## pytest 介绍 pytest是python的一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高。根据pytest的官方网站介绍,它具有如下特点: * 非常容易上手,入门简单,文档丰富,文档中有很多实例可以参考 * 能够支持简单的单元测试和复杂的功能测试 * 支持参数化 * 执行测试过程中可以将某些测试跳过,或者对某些预期失败的case标记成失败 * 支持重复执行失败的case * 支持运行由nose, unittest编写的测试case * 具有很多第三方插件,并且可以自定义扩展 * 方便的和持续集成工具集成 ## pytest 安装 ```cmd pip install -U pytest ``` ## pytest 的使用 ### Pytest测试样例的命名规则 1. 测试文件以test_开头或结尾(否则用py.test命令行不能自动识别) 2. 测试类以Test开头,且不能带有init方法 3. 测试函数以test_开头 4. 断言使用assert 5. fixture的文件名必须是`conftest.py` ### 创建一个最简单的例子 test_sample.py ```python def func(x): return x + 1 def test_answer_right(): assert func(3) == 4 def test_answer_wrong(): assert func(3) == 5 ``` 执行 ```cmd pytest test_sample.py ``` 运行结果如下: ```cmd ============================= test session starts ============================= platform win32 -- Python 3.5.2, pytest-3.6.3, py-1.5.4, pluggy-0.6.0 rootdir: E:\workspace\python_leaning, inifile: collected 2 items test_sample.py .F test_sample.py:12 (test_answer_wrong) def test_answer_wrong(): > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_sample.py:14: AssertionError [100%] ================================== FAILURES =================================== ______________________________ test_answer_wrong ______________________________ def test_answer_wrong(): > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_sample.py:14: AssertionError ===================== 1 failed, 1 passed in 0.09 seconds ====================== ``` 可以发现,执行了两个用例,一个成功,一个失败。 上述命令可以使pytest自动查找 test_sample.py 文件下所有的格式为:`test_*`的方法,然后执行用例.执行顺序为方法的顺序. 如果有多个py文件,可以直接使用下面命令 ``` pytest ``` 这个命令会找出当前目录下所有格式为`test_*.py `或 `*_test.py` 的文件 然后执行用例(文件内格式为`test_*`的方法) ### 在类中分组组织用例 test_sample.py ```python import pytest class TestSample(object): @pytest.fixture() def count(self): print('init count') return 10 def test_answer_1(self, count): print('test_answer_1 get count: %s' % count) assert count == 10 def test_answer_2(self, count): print('test_answer_2 get count: %s' % count) assert count == 10 ``` 运行命令 ``` pytest -s -q ``` `-s`: 是为了输出打印日志,如果不输入-s,是没有日志输出的。 `-q`:quiet 报道模式,这样输出会好看点 运行结果如 ```cmd init count test_answer_1 get count: 10 .init count test_answer_2 get count: 10 . 2 passed in 0.05 seconds ``` ## fixtures 说到测试框架自然要说到setup和teardown两个方法。 * setup:是用来做准备操作.一般用来初始化资源. * teardown:是用来做收尾操作.一般用了关闭资源. pytest的setup和teardown是利用`@pytest.fixture`这个注释来完成的.不仅可以完成初始化操作,初始化后如果有数据需要传给用例使用也是非常方便! 通过`@pytest.fixture() `注释会在执行测试用例之前初始化操作.然后直接在测试用例的方法中就可以拿到初始化返回的参数(<span style="color:red;">参数名</span>要和<span style="color:red;">初始化的方法名</span>**一样**) 在这个实例中,初始化的方法名为`count`,初始化后返回 10。当在测试用例方法中传入与初始化方法名同为`count`的参数时,相对于传入参数`count=10` ### 在fixtures中控制setup与teardown test_sample.py: ```pyhon import pytest class TestSample(object): @pytest.fixture() def count(self,request): print("init count") def fin(): print("teardown count") request.addfinalizer(fin) # teardown return 10 # provide the fixture value def test_answer_1(self, count): print('test_answer_1 get count: %s' % count) assert count == 10 def test_answer_2(self, count): print('test_answer_2 get count: %s' % count) assert count == 10 ``` 运行命令 ``` pytest -s -q``` ```cmd init count test_answer_1 get count: 10 .teardown count init count test_answer_2 get count: 10 .teardown count ``` 在fixture方法中, request.addfinalizer(),会将某函数作为teardown,在用例执行之后执行。 ### fixture 的参数scope `fixture` 有一个域(`scope`)的概念,用来指定该 `fixture` 的使用范围. 它有五种作用域,如`session`/`module`/`class`/`function`(default). - function:每个测试函数之前执行一次 - class:每个测试类之前执行一次 - module:每个module之前执行一次 - session:每次session之前执行一次,即每次测试执行一次 观察上面例子的运行结果,可以发现`init count`与`teardown count` 都打印了两次,那是因为`fixture`默认域是`function`,即每执行一个用例方法前,都会执行初始化方法一次。 为了演示`fixture`的作用域,这里修改一下工程的目录结构,将`fixture`放到一个名称为`conftest.py`(这是pytest规定的命名)的文件中. >[info] 如果一个fixture需要共享,不用在每个py文件中写一遍,写在conftest.py文件中就好。 `conftest.py` ```python import pytest @pytest.fixture(scope="session") def count(): print("init count") yield 10 print("teardown count") ``` `test_sample.py`: ```python class TestSample(object): def test_answer_1(self, count): print('test_answer_1 get count: %s' % count) assert count == 10 def test_answer_2(self, count): print('test_answer_2 get count: %s' % count) assert count == 10 ``` 运行命令 ``` pytest -s -q``` ```cmd init count test_answer_1 get count: 10 .test_answer_2 get count: 10 .teardown count 2 passed in 0.06 seconds ``` 这是在使用 seesion 域的情况下,可以发现`init count` 与 `teardown` 只执行一次.假如我们需要这种情况,每一个类都执行一次初始化方法,那么很简单直接把域改成 class 即可,同理 module 是基于模块的. ### fixture 的参数params 在`test_sample.py`中添加一个带params的fixture,并且在用例中调用这个fixture ```python import pytest @pytest.fixture(params=[1, 2, 3]) def test_data(request): return request.param class TestSample(object): @pytest.mark.skip() def test_answer_1(self, count): print('test_answer_1 get count: %s' % count) assert count == 10 @pytest.mark.skip() def test_answer_2(self, count): print('test_answer_2 get count: %s' % count) assert count == 10 def test_answer_3(self, test_data): print('test_data: %s' % test_data) assert test_data in [1, 2, 3] ``` 执行命令```pytest -s -q``` ```cmd test_data: 1 .test_data: 2 .test_data: 3 . 3 passed, 2 skipped in 0.05 seconds ``` 这里通过`@pytest.mark.skip()`跳过了一些不想要执行的用例 通过执行结果可知,fixture还可以带参数,把参数赋值给params,默认是None。对于param里面的每个值,fixture都会去调用执行一次,就像执行for循环一样把params里的值遍历一次。 ## 用例运行顺序 pytest-ordering pytest按照在.py中的出现位置(`从上到下`)执行用例,可以通过`pytest-ordering` 插件来改变执行顺序 ### 安装pytest-ordering ```cmd pip install pytest-ordering ``` ### 在用例前,使用装饰器 `@pytest.mark.run` 指定用例顺序 当用例中存在 `@pytest.mark.run` 时,先按照order从小到大执行,然后再从上往下执行。 ```python import pytest @pytest.fixture(params=[1, ]) def test_data(request): return request.param class TestSample(object): @pytest.mark.run(order=2) def test_answer_1(self, count): print('test_answer_1 get count: %s' % count) assert count == 10 @pytest.mark.run(order=1) def test_answer_2(self, count): print('test_answer_2 get count: %s' % count) assert count == 10 def test_answer_3(self, test_data): print('test_data: %s' % test_data) assert test_data in [1, 2, 3] ``` 执行命令 ```pytest -s -q``` ```cmd init count test_answer_2 get count: 10 .test_answer_1 get count: 10 .test_data: 1 .teardown count 3 passed in 0.09 seconds ``` ## pytest生成html测试报告 pytest-html pytest-html是py.test的一个插件,它为测试结果生成一个HTML报告。 ### 安装pytest-html ```cmd pip install pytest-html ` ``` ### 运行测试用例 ```cmd pytest --html=report.html ``` 运行后,在当前目录下生产`report.html`,如下 ![](https://box.kancloud.cn/7cae74e0235dfe2869b9a7806c330fc4_1388x565.jpg) ## 在代码中执行用例 有些时候需要在代码中调用执行case的方法.而不是通过command的方式.我们可以在python代码中加入下面代码即可: ```python args = ['-s', '-q'] pytest.main(args) ``` ## 参考文档 作为pytest新手入门,先快速掌握如上部分内容,更多有用的pytest内容,后续会继续更新。 官方文档: [https://docs.pytest.org/en/latest/contents.html](https://docs.pytest.org/en/latest/contents.html) <hr style="margin-top:100px"> :-: ![](https://box.kancloud.cn/2ff0bc02ec938fef8b6dd7b7f16ee11d_258x258.jpg) ***微信扫一扫,关注“python测试开发圈”,了解更多测试教程!***