一、简介
pytest是python的一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高
二、 pytest单元测试框架
a. 特点
1. pytest是python当中的一个单元框架,比unittest更灵活
2. pytest和selenium,requests,appium结合实现web自动化,接口自动化,app自动化
3. pytest可以实现测试用例的跳过以及reruns失败用例重试
4. pytest可以和allure生成非常美观的测试报告
5. pytest可以和Jenkins持续集成
b. pytest插件
1. pytest
2. pytest-html 生成html格式的自动化测试报告
3. pytest-xdist 测试用例分布式执行,多CPU分发
4. pytest-ordering 用于改变测试用例的执行顺序
5. pytest-rerunfailures 用例失败后的重跑
6. allure-pytest 用于生成allure的报告
c. pytest插件安装
1. 全局安装, 这里以windows为例,首先需要安装好python环境, 然后设置pip为全局环境变量,新建一个requirements.txt,将所有插件按顺序复制到txt文档中,然后打开cmd运行 pip install -r 绝对路径\requirements.txt来安装插件即可
2. 虚拟环境安装(具体可以网上搜一下如何创建虚拟环境),然后使用相同的命令安装,这时候这些插件就只能作用于当前工作目录
d. pytest使用规则
1. 模块名必须以test_开头或者_test结尾
2. 测试类必须以Test开头,并且不能有init方法
3. 测试方法必须以test开头
e. pytest测试用例运行模式
主函数模式
1. 运行所有 pytest.main()
2. 指定模块 pytest.main(['-vs', 'module']) # 模块为可选,如果没有默认运行当前项目下的所有用例
3. 指定目录 pytest.main(['-vs', './path']) # 运行的主脚本需要在整个项目根目录下,然后指定目录
4. 通过nodeid指定用例运行: nodeid由模块名,分隔符,类名,方法名,函数名组成
例: pytest -vs ./目录/xx.py::类名::方法名 # 4方法不常用
示例:
pytest.main() # 运行所有
pytest.main(['-vs', 'test_module']) # 运行指定模块
pytest.main(['./test_path']) # 运行指定目录
命令行模式
1. 运行所有 pytest
2. 指定模块 pytest -vs xx.py #模块为可选,如果没有默认运行当前项目下
3. 指定目录 pytest -vs ./目录 #运行的主脚本需要在整个项目根目录下,然后指定目录
示例:
pytest # 运行所有
pytest xx.py # 运行指定模块
pytest './test_path' # 运行指定目录
通过读取pytest.ini配置文件运行
pytest.ini 是pytest单元测试框架的核心配置文件
1. 位置: 一般存放在项目的根目录
2. 编码:必须是ANSI, 可以用Notepad++修改编码格式
3. 作用:改变pytest默认的行为, 例如文件名可以不以Test_开头,类名,函数名皆可
4. 运行的规则:不管是主函数模式运行,还是命令行运行模式,都会去读取这个配置文件。
5. pytest.ini格式:
[pytest]
# 命令行参数, 用空格分隔,可选
# 测试用例文件夹,可自己配置,可选
# 配置测试搜索的模块文件名称,可选
# 配置测试搜索的测试类名,可选
# 配置测试搜索的测试函数名,可选
注: 配置文件中不要保留中文,这里只是注释,编码ANSI会乱码
参数解释
-s: 表示输出调试信息,包括print打印的消息
-v: 显示更详细的信息
-vs: 两个参数可以一起使用
-n process_num: 支持多线程或者分布式运行用例 # 需要插件pytest-xdist
--reruns num:用例失败后重新执行 # 需要插件pytest-rerunfailures
-x: 表示只要一个用例报错则测试停止
--maxfail=2: 出现两个用例失败就停止
-k: 根据测试用例的部分字符串指定测试用例
--html xxx.html:生成html的测试报告
f. pytest执行顺序
1. pytest 默认执行顺序是自上而下
2. 通过装饰器 @pytest.mark.run(order=1)来指定顺序,当有多个装饰器指定顺序一样时,按照自上而下执行,此装饰器需要安装插件pytest-ordering
g. 分组执行 markers
markers是pytest里面用于区分测试用例的标签,主要有两种使用方法
1. 命令行模式 pytest -vs -m "标签名"
2. pytest.ini文件中增加 markers[标签名: 标签说明]
[pytest]
...... # 前面内容省略
markers:label1:标签说明
label2:标签说明
示例:
import pytest
class TestCase:
@pytest.mark.label1
xxxx
@pytest.mark.label2
def test_02(self):
xxxx
命令行执行: pytest -vs -m ”label“ or pytest -vs -m "label1 or label2"
如果使用pytest.ini 则直接pytest -vs即可
h. 跳过测试用例
有条件跳过
@pytest.mark.skipif(condition, reason)
示例:
age =18
@pytest.mark.skipif(age >=18, reason ='已成年')
def xxx(self):
xxx
#条件达成True,跳过测试用例,反之false,执行用例
无条件跳过
@pytest.mark.skipif(reason)
@pytest.mark.skipif('理由')
@pytest.mark.skipif(reason ='理由')
# 两种方式皆可
i. pytest测试夹具(前置后置)
setup/teardown
import pytest
class Test:
def setup(self):
print('测试用例执行前执行:打开浏览器')
def test_01(self):
print('测试用例一')
def teardown(self):
print('测试用例执行后执行: 关闭浏览器')
setup_class/teardown_class
import pytest
class Test:
def setup_class(self):
print('在每个类执行前的初始化操作: 比如创建日志对象,创建数据库链接')
def test_01(self):
print('测试用例一')
def teardown_class(self):
print('在每个类执行后的扫尾操作: 比如销毁日志对象,关闭数据库链接')
@pytest.fixture 装饰器
@pytest.fixture(scope=", params=", ids='', names='', autouse=False)
scope
scope表示的是被@pytest.fixture标记的方法的作用域
1. function
示例
@pytest.fixture(scope='function') # 类似于setup/teardown
import pytest
@pytest.fixture(scope='function')
def my_fixture():
print('这是前后置方法,可以实现部分以及全部用例的前后置')
yield
print('后置测试方法, 用例执行完之后执行')
class Test:
def test_01(self, my_fixture):
print('测试一')
示例
@pytest.fixture(scope='function',autouse=True)
下面用例就无需添加my_fixture,每个用例执行前后都会执行my_fixture函数, autouse= False则不执行
import pytest
@pytest.fixture(scope='function', autouse=True) #类似于setup/teardown
def my_fixture():
print('这是前后置方法,可以实现部分以及全部用例的前后置')
yield
print('后置测试方法, 用例执行完之后执行')
class Test:
def test_01(self):
print('测试一')
结果: 在用例执行前后执行my_fixture函数
2. class
示例
@pytest.fixture(scope='class',autouse=True) # 类似于setup_class/teardown_class
scope为class的时候,必须配autouse=True,因为autouse默认是False,不附上则不执行
import pytest
@pytest.fixture(scope='class', autouse=True)
def my_fixture():
print('这是前后置方法,可以实现部分以及全部用例的前后置')
yield #带yield的函数是一个生成器,附上要return生成内容即可
print('后置测试方法, 用例执行完之后执行')
class Test:
def test_01(self):
print('测试一')
class Test1:
def test_02(self):
print('测试二')
结果: 在Test类的前后执行my_fixture函数,如果有多个类,就每个类的前后执行前置后置一次
3. Module
示例
@pytest.fixture(scope='module',autouse=True)
scope为module的时候,必须配autouse=True,因为autouse默认是False,不附上则不执行
import pytest
@pytest.fixture(scope='module', autouse=True)
def my_fixture():
print('这是前后置方法,可以实现部分以及全部用例的前后置')
yield #带yield的函数是一个生成器,附上要return生成内容即可
print('后置测试方法, 用例执行完之后执行')
class Test:
def test_01(self):
print('测试一')
class Test1:
def test_02(self):
print('测试二')
结果: 在Test类的前和Test1类后执行my_fixture函数
4. package/session
这两种用的比较少,以后用到再更新
params
参数化, 支持 [1. list[],2. tuple(),3. list_dict[{},{},{}],4. tuple_dict({},{},{}) ]
示例
import pytest
@pytest.fixture(scope='function', params=['年','月','日']) #params是参数名
def my_fixture(request): #request为固定写法
return request.param #param是属性名
class Test:
def test_01(self, my_fixture):
print('测试一')
Print('----------' + str(my_fixture)) #这里需要强制转换成str
结果: 在方法test01每次执行后,打印年,月,日的信息
注: 如果不参数化的时候可以直接return一个字符串, 当该函数继续支持前后置的时候可以使用以下代码
import pytest
def my_fixture(request):
print('前置')
yield request.param #return和yield都表示返回的意思,但是return后面不能有代码,但yield可以
print('后置')
ids
使用params参数化时,给每一个值设置一个变量来替换结果中输出的unicode
@pytest.fixture(scope='function', params=['年', '月' ,'日'], ids=['nian' ,'yue' ,'ri'])
name
给表示被@pytest.fixture标记的方法取一个别名
注: 当设置别名后原本的名称就不能再当参数使用了
import pytest
@pytest.fixture(scope='function', name='aaa')
class Test:
def test_01(self, aaa):
print('测试一')
autouse
是否自动执行,默认为False,True则自动执行
不常用的另一种方法
conftest.py和@pytest.fixture结合使用全局的前置应用
conftest.py文件是单独存放的一个夹具配置文件,名称不能修改
可以在不同的py文件中使用同一个fixture函数
原则上 conftest.py和需要调用的用例在同一层目录,无须import操作
可以每个模块全局,也可以整个项目全局,注意这种使用情况的话,调用顺序为先项目全局再模块全局
总结
1. setup/teardown, setup_class/teardown_class 作用于所有用例或者类
2. @pytest.fixture() 作用可是是部分也可以全局前后置
3. conftest.py和@pytest.fixture结合使用, 作用域全局的前后置
j. 断言
assert condition
k. pytest+allure测试报告
1. 环境搭建-Windows
手工下载: https://github.com/allure-framework/allure2/releases/,下载完成后设置添加系统全局变量指向到allure插件的bin目录,例如 path: C:\allure-xx\bin
验证添加是否成功,打开cmd,运行allure --version,查看是否添加成功
2. 生成json格式临时报告
命令行模式 pytest -vs --alluredir ./temp
pytest.ini文件
[pytest]
addopts = -vs --alluredir ./temp
.....
3. 生成allure的报告
allure generate ./temp -o ./report --clean
参数解释
./temp 临时json格式的报告路径
-o 输出output参数
./report 生成allure报告的路径
--clean 清空./report路径原来的报告
l. @pytest.mark.parametrize装饰器
@pytest.mark.parametrize(args_name, args_value),一般有两种用法
args_name: 参数名
args_value: 参数值(list, tuple,list_dict, tuple_dict), 有多个值用例就会执行多次
1. 第一种用法
传入一个list,然后查看结果,将整个列表 ”年 月 日“打印出来
import pytest
class TestApi:
@pytest.mark.parametrize('args',['年', '月', '日']) # 传入了一个list
def test_01(self, args):
print(args)
if __name__='__main__':
pytest.main()
2. 第二种用法
和unittest里面ddt的@unpack解包一致,将两个列表item1,item2依次传入test01用例,结果打印两次分别是”年 月 日“,”小时 分 秒“
import pytest
class TestApi:
@pytest.mark.parametrize('args, item1, item2', [['年', '月', '日'],['小时', '分', '秒']])
def test_01(self, args, item1, item2):
print(args, item1, item2)
if __name__='__main__':
pytest.main()
m. yaml文件--实现接口自动化
yaml是一种数据格式,支持注释,换行, 多行字符串,裸字符串(整形,字符串),需要安装选择PyYAML插件
1. 用于全局的配置文件 ini/yaml
2. 用于写测试用例(接口测试用例)
语法规则
区分大小写
使用缩进表示层级,不能使用tab键缩进,只能用空格(和python一样)
缩进没有数量限制,只要前面是对齐的就行
注释也是#
数据组成
Map对象:
格式:字典键值对
一行写法:
test:{name: '名称', age: '年龄'}多行写法:
test:
name: '名称'
age: '年龄'数组:
格式:列表字典键值对
一行的写法
-
test: [{name: ’名称‘},{age: ’年龄‘}]多行写法:
-
test
- name: ‘名称’
- age: ’年龄‘
-
test1:
- name: ’名称‘
-
接口自动化实例:
yaml文件就以字典键值对为例
test:{name: '名称', age: '年龄'},确保文件和脚本在同一目录,这里python运行即可,不要用pytest
import yaml
class YamlUtil:
def __init__(self, yaml_file):
"""
通过init方法将yaml文件传入这个类
:param yaml_file:
"""
self.yaml_file = yaml_file
def read_yaml(self):
"""
读取yaml, 对yaml反序列化, 就是把yaml格式转换成dict格式
:return:
"""
with open(self.yaml_file, encoding='utf-8') as file_object:
value = yaml.load(file_object, Loader=yaml.FullLoader)
print(value)
if __name__ == '__main__':
YamlUtil('test_api.yaml').read_yaml()
结果: {'test': {'name': '姓名', 'age': '年龄'}