目录
一、 pytest单元测试框架
- 什么是单元则试框架
单元测试是指在软件开发当中,针对软件的最小单位(函数,方法)进行正确性的测试 - 单元则试框架
java: junit和 testng
python: unittest和pytest - 单元则试框架主要做什么?
1、测试发现:从多个文件里面去找到我们测试用例
2、则试执行:按照一定的顺字和规则去执行。井生成结果
3、测试判断:通过断言判断预明结果和实际结果的差异
4、测式报告:統计题试进度,耗时,通过率,生成测试报告
二、pytest简介
- pytest是一个非常成熟的 python的单元框架,比 unittest更灵活,容易上手
- pytest可以和 selenium, requests, apium结合实现web自动化,接口自动化,app目动化。
- pytest可以实现测试用例的跳过以及 失败用例重试
- pytest可以和 allure:生成非常美观的测试报吉
- pytest可以和 Jenkinsi持集成
- pytest有很多非第强大的插件,井且这些插件能够实现很多的实用的操作
pytest
pytest-html #生成htm格式的目动化测试报告
pytest-xdist #测试用例分布式执行。多CPU分发
pytest-ordering #用于改变测试用例的执行顺序
pytest-rerunfailures #用例失败后重跑
allure-pytest #用于生成美观的测试报告
放到 requirements.txt中,通过 pip install -r requirements.txt
allure下安装参考:安装方法
安装好后安装allure-pytest插件
三、使用 pytest,默认的测试用例的规则以及基础应用
- 模块名必须以test开头或者test吉尾
- 试类必须以Test开头,并且不能有init方法。
- 测试方法须以test开头
四、pytest测试用例的执行方式
-
主数榎式
(1)运行所有: pytest.main()
(2)指定模块: pytest.main("-vs’, test_login.py])
(3)指定目录: pytest. main(’-vs’, ’ /interface_ testcase)
(4)通过 nodeid指定用例运行: nodeid由模块名,分隔行,类名,方法名,数名组成
pytest. main(’-vs’, / interface_testcase/test interface py. 04 func))
pytest main(’-vs’ /interface_testcase/test interface::pytestintertace_03_zhiliao) -
命令行榎式
(1)运行所有: pytest
(2)指定模块: pytest- vs test_login. py
(3)指定目录: pytest- vs /interface_ testcase
(4)指定目录: pytest- vs / interface_ testcase/test_interface. py::test_04_func -
参数详解
-S:表示输出试信息,包括 print打印的信思
v:显示更细的信息
-vs:这两个参数数一起用
-n:支持多线程或分布式运行测试用例。
如: pytest -vs /testcase/test_login.py -n 2
reruns NUM:失败用例重跑
X:表示只要要一个用例服错,那么测试停止
-maxair=2 出现两个用例失败就停止
k:根据测试用例的部分字符串指定测试用例。
如: pytest -vs/ testcase- K "ao”
–html ./report/report.html :生成HTML的报告
- 通过取 pytest.ini 配置文件
pytest.ini 这个文件它是 pytest单元测试框架的核心配置文件
1、位置:一放在而国的目录
2、必须是ANSI,可以便用 notpad++ 修改编码格式
3、作用:改变 pytest认的行为
4、运行的观则:不管主函数的模式运行、命令行模式运行、都会去读取这个配文併。
[pytest]
addopts = -vs --html ./report/report.html #命令行的参数,用空格分隔
testpaths = ./testcase #测试用例的路径
python_files= test_*.py #模块名的规则
python_classes= Test* #类名的规则
python_functions= test* #方法名的规则
五、pytest执行测试用例的顺序
unittest :asclla的大小来绝对的执行的顺序
pytest:默认从上到下
改变默认的执行顺序:使用mark标记
@pytest.mark.run(order=3) 数值代表优先级,由1开始
六、如何分组执行(冒烟,分模块执行,分接口和web执行)
smoke:冒烟用例,分布在各个模块里面
pytest -m “smoke”
pytest -m “smoke or usermanage or productmanage”
七、跳过测试用例
- 无条件跳过
@pytest.mark.skip(reason='测试') #reason 写跳过的原因
- 有条件跳过
@pytest.mark.skipif(age>=18,reason='已成年')
八、pytest框架实现一些前后置(夹具,固件)的处理,常用三种方式
- setup/teardown,setup_class/teardown_class
import pytest
class Test_login:
#在每个用例之前执行一次
def setup(self):
print('在执行用例之前的初始化代码:打开浏览器')
#在所有的用例之前只执行一次
def setup_class(self):
print('在每个类执行前的初始化工作:比如创建日志对象,创建数据库连接,创建接口的请求对象')
def teardown(self):
print('在执行测试用例后的执行:比如关闭浏览器')
def teardown_class(self):
print('在每个类执行后执行:比如销毁日志对象,关闭数据库连接')
@pytest.mark.run(order=3)
@pytest.mark.smoke
def test_1(self):
print('测试')
@pytest.mark.run(order=1)
@pytest.mark.usermanage
def test_2(self):
print('测试')
@pytest.mark.skip
def test_3(self):
print('测试')
def test_4(self):
print('测试')
if __name__ == "__main__":
pytest.main(['-vs','./','--reruns=2']) #--reruns=2 表示失败的地方重跑两次
- 二、使用@pytest.fixture()装饰器来实现部分用例的前后置
@pytest.fixture(scope="", params="", autouse="",ids="",name="")
(1) scope表示的是被@pytest.fixture标记的方法的作用域 ,function(默认), class, module,package/session
== function:每个方法执行一次(autouse=True的时候)
class:每个类执行一次
module:每个py文件执行一次
package/session:
==
(2) params:参数化(支持列表,元组,字典列表,字典元组)
(3) autouse=True:自动执行,默认 False
(4)ids:当使用 params参数化时,给每一个值设置一个变量名。意义不大。
(5)name:始表示的是被@ pytest.fixture标记的方法取一个别名
#####scope的使用
import pytest
@pytest.fixture(scope='function')
def my_fixture():
print('这是前置的方法')
yield
print('这是后置的方法')
class Test_one:
def test_01(self):
print('01')
def test_02(self,my_fixture):
print('02')
if __name__ == '__main__':
pytest.main(['-vs','./test_01.py'])
================运行结果=======================
test_01.py::Test_one::test_01 01
PASSED
test_01.py::Test_one::test_02 这是前后置方法
02
PASSED
============================== 2 passed in 0.02s ==============================
####params的使用
import pytest
@pytest.fixture(scope='function',params=['test1','test2','test3'])
def my_fixture(request): #使用request 固定写法
print('这是前置的方法')
yield
print('这是后置的方法')
return request.param #param此时没有s
class Test_one:
def test_01(self):
print('01')
def test_02(self,my_fixture):
print('02')
print('------------'+str(my_fixture))
if __name__ == '__main__':
pytest.main(['-vs','./test_01.py'])
==========================打印结果=======================
test_01.py::Test_one::test_01 01
PASSED
test_01.py::Test_one::test_02[test1] 这是前置的方法
02
------------None
PASSED这是后置的方法
test_01.py::Test_one::test_02[test2] 这是前置的方法
02
------------None
PASSED这是后置的方法
test_01.py::Test_one::test_02[test3] 这是前置的方法
02
------------None
PASSED这是后置的方法
============================== 4 passed in 0.03s ==============================
- 通过 conftest.py和@pytest.fixture() 结合使用实现全局的前置应用(比如:项目的全局登录,模块的全局处理)
1、 conftest. py文件是单独存放的一个夹具配置文件,名称是不能更改
2、用处可以在不同的py文件中使用同一个fixture图数
3、原则上 conftest.pyi需要和运行的用例放到统一层。并且不需要做任何的import导入的操作
总结
setup/teardown, setup_ class/teardown_class它是作用于所有用例或者所有的类
@ pytest.fixtrue() 它的作用是既可以部分也可以全部前后
contest.py和@pytest.fixtrue()结合使用,作用于全局的前后置
#######test_one.py里面的代码
class Test_one:
def test_01(self,cc):
print('01')
def test_02(self):
print('02')
#############test_two.py里面代码
class Test_two:
def test_01(self):
print('01')
def test_02(self,cc):
print('02')
print("---------------------"+str(cc))
#########conftest.py里面的代码
import pytest
@pytest.fixture(scope='function',params=['cc'],name='cc')
def my_fixture(request): #使用request 固定写法
print('这是前置的方法')
yield
print('这是后置的方法')
return request.param #param此时没有s
#############run里面的代码
import pytest
if __name__ == '__main__':
pytest.main(['-vs','./test_case/manage'])
#######################运行结果#################################
test_case/manage/test_01.py::Test_one::test_01[cc] 这是前置的方法
01
PASSED这是后置的方法
test_case/manage/test_01.py::Test_one::test_02 02
PASSED
test_case/manage/test_02.py::Test_two::test_01 01
PASSED
test_case/manage/test_02.py::Test_two::test_02[cc] 这是前置的方法
02
---------------------None
PASSED这是后置的方法
============================== 4 passed in 0.03s ==============================
九、断言
assert
assert 1==2
十、pytest结果allure-pytest插件生成allure测试报告
-
下载,解压,配path路径
https://github.com/allure-framework/allure2/releases
path路径配置
验证: allure-- version
pycharm需要重启才能获取到 -
加入命令生成json格式的临时报告
–alluredir ./temp -
生成 allure报告
os.system( ‘allure generate ./temp -o report -clean’)
allure generate 命令,固定的
./temp 临时的json格式报告的路径
-0 输出 output
/report 生成的 allure报告的路径
-clarn 清空 report路径原来的报吉
###############pytest.ini的代码
[pytest]
addopts = -vs --alluredir ./temp
testpaths = ./
python_files = test_*.py
python_classes = Test*
python_functions = test*
markers =
smoke:冒烟测试
usermanage:用户管理模块
###########run文件代码
import pytest
import os
if __name__ == '__main__':
pytest.main(['-vs','./test_case/manage'])
os.system('allure generate ./temp -o report --clean')
生成的报告文件
十一、补充—pytest中的装饰器@ pytest.mark. parametrize()的基础用法。
一、 数据驱动:其实就是把我们测试用例的数据放到 excel,yaml,csv, mysql.然后通过去改变数据达到改变测试用例的执行结果
@pytest.mark.parametrize(args_name, args_value)
args_name:参数的名称
args_value:数的值 (list,tuple,字典列表,字典元组)
pytest默t认的测试用例的规则( pytest ini)
1、模块名必须以test或音是_test开头
2、类名必须以Test开头
3、方法名必须以test开头
import pytest
class Test_api:
#最基础的用法
@pytest.mark.parametrize('cc',['test1','test2','test3'])
def test_01(self,cc):
print(cc)
#解包的方法
@pytest.mark.parametrize('name,age',[['test1','18'],['test2','20'],['test3','21']])
def test_02(self,name,age):
print(name,age)
if __name__ == '__main__':
pytest.main(['-vs','./test.py'])
#####运行结果#################
test.py::Test_api::test_01[test1] test1
PASSED
test.py::Test_api::test_01[test2] test2
PASSED
test.py::Test_api::test_01[test3] test3
PASSED
test.py::Test_api::test_02[test1-18] test1 18
PASSED
test.py::Test_api::test_02[test2-20] test2 20
PASSED
test.py::Test_api::test_02[test3-21] test3 21
PASSED
============================== 6 passed in 0.04s ==============================
二、 yaml文件语法规则详解
- yaml简介
yaml它是一个数据文件,保存的一个数据格式,支持注释,换行,裸字符串(最小单位的数据) - yaml用法
(1)用于全局配置文件:环境,数据库信息,账号信息,日志格式,报告名称
(2)用于接口目动化里面的多一些复杂的多接口串联
(3)用于编写接口测试用例 - YAML语法规则
(1)区分大小写
(2)和 pythonー样也是通过缩进的方式来表示层级关系(不同的是不能使用ab縮进,只能用空格)
(3)和缩进多少层无关,只和左边是否对齐有关系
(4)#表示注释 - YAML数组组成车例
1.map对象健(空格)值
2.数组(列表):用一组横线开头
实例:
##################################yaml内容:
-
api_name: 获取网易新闻
api_request:
url: https://api.apiopen.top/getWangYiNews
method: post
headers:
Content-type: application/json
params:
page: 1
count: 5
api_valldate:
- eq: {code: 200,message: '成功'}
##################################读取yaml文件代码:
import yaml
def read_yaml(yaml_file):
"""
读取yaml文件
:return:
"""
with open(yaml_file,encoding='utf-8') as f:
data = yaml.load(f, Loader=yaml.FullLoader)
return data
##############################################用例:
import pytest
from Common.read_yaml import read_yaml
import requests
class Test_api:
@pytest.mark.parametrize('args',read_yaml('./case_01.yaml'))
def test_wy(self,args):
url = args['api_request']['url']
method = args['api_request']['method']
headers = args['api_request']['headers']
params = args['api_request']['params']
assertion = args['api_valldate']
if method == 'get':
res = requests.get(url)
else:
res = requests.post(url=url,json=params,headers=headers)
for i in assertion:
if i['eq']['code'] == res.json()['code'] and i['eq']['message'] == res.json()['message']:
print('测试成功')
if __name__ == '__main__':
pytest.main(['-vs','./test.py'])
十二、补充----allure自定义报告
使用方法 | 参数值 | 参数说明 |
---|---|---|
@alllure.epic() | epic描述 | 项目描述,往下是feature |
@allure.feature() | 模块名称 | 功能点的描述,往下是story |
@allure.story() | 用户故事 | 用户故事,往下是title |
@allure.title() | 用例的标题 | 重命名HTML报告的名称 |
@allure.testcase() | 测试用例的链接地址 | 对应测试用例系统里面的case |
@allure.issue() | 缺陷 | 对应缺陷管理系统里面的链接 |
@allure.description() | 用例描述 | 测试用例的描述 |
@allure.step() | 操作步骤 | 测试用例的步骤 |
@allure.severity() | 用例等级 | blocker、critical、normal、minor、trivial |
@allure.link() | 链接 | 定义一个链接,在测试报告展示 |
@allure.attachment() | 附件 | 报告添加附件 |