PyTest
一、简介
单元测试框架
测试发现
测试执行
测试判断(断言判断预期记过和实际结果的差异)
测试报告(进度,耗时,通过率)
自动化测试框架∈单元测试框架
pytest可以和selenium,requests。appium结合实现web自动化,接口自动化, app自动化
pytest-html:生成html的自动化测试报告
pytest-xdist:测试用例分布式执行,多cpu分发
pytest-ordering:改变测试用例执行顺序
pytest-rerunfailures:用例失败后重跑
- -reruns times --reruns-delay time
allure-pytest:用于生成测试报告
pytest-repeat
- -count=2 --repeat-scope=module
二、默认测试用例规则以及基础应用
- 模块名必须以test_开头或者_test结尾
- 测试类必须以Test开头,并且不能有init方法
- 测试方法必须以test开头
三、运行方式
- 主函数方式
- 运行所有:pytest.main()
- 运行指定模块:pytest.main(["参数","运行模块"])
- 指定目录:pytest.main(["参数","./test_1.py"])
- 通过nodeid:pytest.main(["参数","./test_1.py::Test01::test_04"])
- 命令行方式
- 同主函数方式
- pytest 参数 运行模块
- 通过读取pytest.ini配置文件运行
- 位置:一般放在项目根目录
- 编码:ANSI
- 作用:改变pytest默认行为
- 运行的规则:主函数和命令行规则都会读取该文件
- 格式:
[pytest] #命令行参数,用空格分隔 addopts = -vs #测试用例文件夹,可自行配置 testpaths = ./test_ #配置测试搜索的模块文件名称 python_files = test_*.py #配置测试搜索的测试类名 python_classes = Test* #配置测试搜索的测试函数名 python_functions = test #分组 markers = smoke:冒烟用例 usermanage:用户管理
参数详解:
- s:输出调试信息,包括print打印的信息
- v:显示更详细的信息
- vs:两个参数一起使用
- n:支持多线程或者分布式运行 pytest.main(['-vs','test_01.py','-n=2'])/pytest test_01.py -n 2
- -reruns NUM:失败用例重跑2次 pytest test_01.py -reruns 2
- x:只要有一个测试用例失败则所有重跑
- -maxfail=2:最大失败测试用例就停止
- k:根据测试用例的部分字符串指定测试用例 pytest -vs /testcase -k 'te'
- -html 目录+文件名.html:生成报告
四、执行顺序
- 默认从上到下执行
- @pyrest.mark.run(order=NUM)
五、分组执行(冒烟、分模块执行、分接口和web执行)
- smoke:冒烟用例
@pytest.mark.smoke
pytest -m smoke pytest -m "smoke and/or usermanage" pytest -m "smoke and not usermanage"
六、pytest跳过用例
- 无条件跳过
@pytest.mark.skip(reason='想跳过')
2.有条件跳过
@pytest.mark.skipif(age>=18,reason='已成年')
七、前后置(固件、夹具)处理
- setup/teatdown,setup_class/teardown_class
def setup():#在每个用例之前执行一次 print("在测试用例之前/之后执行的代码:打开浏览器,加载网页") def setup_class():#在所有用例之前只执行一次 print("在每个类执行前的初始化工作:创建日志对象,创建数据库的连接,创建接口的请求对象") def teardown(): print("在执测试用例之后扫尾的代码:关闭浏览器") def teardown_class(): print("在每个类执行后的扫尾的工作:销毁日志对象,销毁数据库的连接,销毁接口的请求对象")
- 使用@pytest.fixture装饰器来实现*部分*用例的前后置
@pytest.fixture(scope="",params="",autouse="",ids="",name="") def my_fixture(): print("这是前置") yield print("这是后置") class Test01(object): """docstring for Test01""" def test_01(self, my_fixture): print("test")
参数详解:
- scope表示的是作用域:在每个作用域执行一次:function(默认),class,module,package/session
- params:参数化,支持list[],元祖(),字典{}
- fixture带['1','2','3']会执行三次
- autouse=True:自动使用fixture,默认False
- ids:当使用params参数化时,给每一个值设置一个变量名,意义不大
- name:给表示的是被@pytest.fixture()标记的方法取一个别名,若起了别名之后,使用原函数名无法调用
传值:
def my_fixture(request): reutrn request.param #注意不带s,一次只传一个,妙啊 def my_fixture(request) print("这是前置") yield request.param print("这是后置") #or: def my_fixture(): return 'a string'
- 通过conftest.py和@pytest.fixture()结合使用实现全局的前置应用(比如:项目的全局登录,模块的全局处理)
- conftest.py是单独存放的一个夹具文件,文件名是不可更改的
- 可以在不同的py文件中使用同一个conftest.py函数
- 不需import
总结:
- setup/teardown,setup_class/teardown_class作用于所有用例或者所有的类
- @pytest.fixture()作用既可以是部分也可以是全部前后置
- conftest.py和@pytest.fixture()结合使用作用于全局的前后置
七、断言
- assert
八、pytest结合allure-pytest插件生成allure测试报告
- 生成json格式的临时报告
-alluredir ./temp_path
- 生成allure报告
allure generate ./temp_path -o ./report_path --clean
临时json 输出 生成报告路径 清空原有报告
九、@pyrest.mark.parametrize()基本用法
@pyrest.mark.parametrize(args_name,params) @pyrest.mark.parametrize('name',['phil','fitz']) @pyrest.mark.parametrize('name,age',[['phil','18'],['fitz','19']])
十、YAML文件详解--实现接口自动化
- 用于全局的配置文件 ini、yaml
- yaml简介:
yaml是一种数据格式,支持注释、换行、多行字符串、裸字符串(整型、字符串)
- 语法规则:
- 区分大小写
- 使用缩进表示层级,但只能用空格
- 缩进是没有数量的,只要对齐就是同一层级
- 注释是#
- 数据组成:
- Map对象:键值对:key:+空格+value
一行的写法:msxy:{name:phil,age:18}
- 数组(list):用-开头
一行的写法:[]
- 读取:转化为字典:
class YamiUtil: def __init__(self, yaml_file) self.yaml_file = yaml_file def read_yaml(self): with open(self.yaml_file, encoding='utf-8') as f: value = yaml.load(f, Loader=yaml.FullLoader) return value
十一、yaml接口自动化实战
- 断言的封装
- allure报告的定制
- 关键字驱动和数据驱动结合实现接口自动化测试
- python的反射
- jenkins的持续集成和allure报告集成,并且根据自动化报告的错误率发送邮件
- 文件和读取
- yaml_file:
- test_Api.py:
name: 获取token的接口 request: url: https://api.weixin.qq.com/cgi-bin/token method: get headers: - Content-Type: application/json params: - appid: wx6b11b3efd1cdc290 - secret: 106a9c6157c4db5f6029918738f9529d validate: - eq: {expires_in: 7200}
import pytest import requests class TestApi(): @pytest.mark.parametrize('args', YamiUtil(yaml_file).read_yaml()) def test_01(self, args): url = args['request']['url'] params = args['request']['params'] res = request.get(url, params=params) assert args['validate']['expires_in'] in res.text