[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测试开发圈”,了解更多测试教程!***
- 前言
- chapter01_开发环境
- chapter02_字符串的使用
- chapter03_列表的使用
- chapter04_字典的使用
- chapter05_数字的使用
- chapter06_元组的使用
- chapter07_集合的使用
- chapter08_输入输出
- chapter09_控制流程
- chapter10_实例练习_登录1
- chapter11_python函数入门
- chapter12_python中的类
- chapter13_轻松玩转python中的模块管理
- chapter14_掌握学习新模块的技巧
- chapter15_通过os模块与操作系统交互
- chapter16_子进程相关模块(subprocess)
- chapter17_时间相关模块(time & datetime)
- chapter18_序列化模块(json)
- chapter19_加密模块(hashlib)
- chapter20_文件的读与写
- chapter21_阶段考核2_登录
- chapter22_小小算法挑战(排序&二分法)
- chapter23_用多线程来搞事!
- chapter24_HTTP接口请求(requests)
- chapter25_接口测试框架(pytest)
- chapter26_阶段考核3_HTTP接口测试
- chapter27_HTML解析(pyquery)
- chapter28_阶段考核4_爬虫下载网易汽车
- chapter29_python中的那些编码坑
- chapter30_MySQL数据库操作
- chapter31 高级特性_迭代器与生成器
- chapter32 高级特性_装饰器
- chapter33 高级特性_列表处理