java:junit,testing
python:unittest,pytest

安装
pip install pytest
升级到最新版
pip install pytest -U
pytest启动的三种方式
def test_login
print('执行login测试用例')
在启动文件main.py
import pytest
pytest.main()
3.鼠标(不推荐),因为是python提供的,不是pytest

执行环境:版本,根目录,用例数量
执行过程:文件名称,用例结果,执行进度
失败详情:用例内容,断言提示
整体摘要:结果情况,结果数量,花费时间
其中用例结果有6种

用例发现规则
测试框架在识别,加载用例的过程,称之为:用例发现
pytest的用例发现规则步骤
案例
#测试函数"""
def add(a,b)
return a+b
class TestAdd:
#测试方法"""
def test_int(self):
res=add(1,3)
assert res == 4
def test_str(self):
res=add("1","3")
assert res == "13"
def test_list(self):
res=add([1],[2,3,4])
assert res == [1,2,3,4]
配置可以改变pytest默认的规则
命令参数ini配置文件
pytest -h
有哪些配置
分别是什么方式
常用参数:

标记,可以让用例与众不同,进而可以让用例被区别对待
1.用户自定义标记
用户自定义标记只能实现用例筛选
步骤:
1.先注册
如:在pytest.ini文件中
[pytest]
marker =
api:接口测试
web:web测试
ut:单元测试
login:登录相关
pay:支付失败
ddt:数据驱动测试
2.再标记
如:
#测试函数"""
def add(a,b)
return a+b
class TestAdd:
#测试方法"""
@pytest.mark.web
def test_int(self):
res=add(1,3)
assert res == 4
@pytest.mark.api
def test_str(self):
res=add("1","3")
assert res == "13"
@pytest.mark.pay
def test_list(self):
res=add([1],[2,3,4])
assert res == [1,2,3,4]
3.后筛选
pytest -m web
结果:只执行了标记了web的测试用例

2.框架内置标记
框架内置标记为用例增加特殊执行效果
和用户自定义标记区别

@pytest.mark.skip
def test_list(self):
res=add([1],[2,3,4])
assert res == [1,2,3,4]
结果:跳过了这条测试用例
数据驱动测试=参数化测试+数据文件
根据数据文件的内容,动态决定用例的数量,内容
1.创建一个data.csv
a,b,c
1,2,3
2,3,4
4,5,6
1,2,4
2.封装函数,数据文件,驱动用例数量和内容
import pytest
#封装函数
import csv
def read_csv(path):
reader=csv.reader(f)
return list(reader)[1:]
#测试函数"""
def add(a,b)
return a+b
#测试用例"""
class TestAdd:
@pytest.mark.ddt
@pytest.mark.parametrize(
"a,b,c"
read_csv("data.csv")
)
def test_list(self,a,b,c):
res=add(int(a),int(b))
assert res == c
1.夹具:在执行之前,执行之后运行代码
场景:
之前:加密参数、之后:解密结果
之前:启动浏览器、之后:关闭浏览器
之前:注册,登录账号、之后:删除账号
1.创建函数
2.添加装饰器
3.添加yield关键字
@pytest.fixture
def f():
print(datatime.now(),'用例开始执行')
#前置操作
yield
#后置操作
print(datatime.now(),'用例结束执行')
2.使用fixture
1.在用例参数列表,加入fixtures名字就可以了
2.给用例加上usefixtures标记
@pytest.fixture
def f():
print(datatime.now(),'用例开始执行')
#前置操作
yield
#后置操作
print(datatime.now(),'用例结束执行')
#使用方式1
@pytest.mark.usefixtures("f")
def test_1():
pass
#使用方式2
def test_2(f):
pass
3.高级用法
1,现实使用
@pytest.fixture(autouse=True)
def f():
print(datatime.now(),'用例开始执行')
#前置操作
yield
#后置操作
print(datatime.now(),'用例结束执行')
def test_1():
pass
#使用方式2
def test_2():
pass
2.依赖使用
linux:使用linux进行编辑git:使用git进行版本控制fixture:使用fixture来进行前后置自动操作
import pytest
@pytest.fixture
def ff():
print('我也是夹具,但是被fixture使用')
@pytest.fixture(autouse=True)
def f(ff):
print(datatime.now(),'用例开始执行')
#前置操作
yield
#后置操作
print(datatime.now(),'用例结束执行')
def test_1():
pass
#使用方式2
def test_2():
pass
运行结果

3.返回内容:接口自动化封装:接口关联
如:用yield传递参数
@pytest.fixture(autouse=True)
def f():
print(datatime.now(),'用例开始执行')
#前置操作 yield传递参数123
yield 123
#后置操作
print(datatime.now(),'用例结束执行')
def test_1(f):
print('收到传递的参数',f)
#使用方式2
def test_2():
pass
4.范围共享:
默认范围:function
全局范围:session
使用conftest.py
pytest 插件生态是pytest的优势之处
插件分为两类
1.不需要安装:内置chajian
2.需要安装:第三方插件
插件的启用和管理
1、启用: -p abc
2,禁用:-p no:abc
插件的使用方式:
参数配置文件fixturemark网址:docs.pytest.org/stable/refernce/plugin_html
pytest-html
作用:生成html的测试报告
安装:
pip install pytest-html
使用:
pytest --html=report:file:///E:/PyProject/my_pytest/report.html
也可以在配置文件按添加,如下:

pytest-xdist
用途:分布式执行
安装:pip install pytest-xdist
使用:
-n N
n:线程
N:表示几个分布式使用
只有在任务本身耗时比较长,超出调用成本很多时候,才有意义
分布式执行,有并发问题:资源竞争、乱序
pytest-reunfailures
用途:用例失败后,重新执行
安装
pip install pytest-reunfailure
使用:
--reruns 5 --reruns-delay 1
pytest-result-log
用途:把用例的执行结果记录到日志文件中
安装:pip install pytest-result-log
使用:

11.企业级测试报告
allure是一个测试报告框架
pip intsall allure-pytest
安装
pip install allure-pytest
配置
--alluredir=temps --clean-allure
生成报告
allure generate -o report -c temps
allure 支持对用例进行分组和关联(敏捷开发术语)
@allure.epic 史诗 项目
@allure.feature 主题 模块
@allure.story 故事 功能
@allure.title 标题 用例
使用相同装饰器的用例,自动并入一组
pytest 仅进行用例管理,不会控制浏览器,需要借助新的工具:selenium
1.只了解selenium
2.搜索关于selenium的pytest插件
案例
@pytest.fixture()
def selenium():
d=webdriver.Chrome()
yield d
d.quit()
封装:隐藏细节,增加功能,优化功能
接口自动化:
1.可以用yaml作为用例,降低门槛
2.自动请求接口,断言接口
3.自动在日志记录http报文
4.自动生成allure测试报告
yaml文件 test_yaml
---
name:访问百度
steps:
- request:#发送请求
method:GET
- url:https://www.baidu.com
- response:#断言响应
status_code: 200
text:'**baidu**'
json:
code:0
data:
banner_list:
- name: 美酒
- extract:#提取变量
code:[ status_code,(.*)]
- validate:# 断言变量
code:200
一句话:完全兼容json格式,并且支持python相似写法
重点:
1.yaml完全兼容json
2,数据格式不是变成语言
3,像python一样容易编辑和阅读
1.安装yaml模块
pip install pyyaml
2.编写yaml文件
1.#作为注释符号
2.缩进:2个空格
3.成员表示:- 表示列表成员 :表示字典成员
4.完全兼容json格式
benfan.yaml
数字:
- 1
- -1
- 1.1
字符串:
-'121221'
- "aadd"
空值:null #json 写法
列表:[1,2,3,] #json写法
字典:{"a":1,"b":2} #json 写法
3.加载yaml文件
benfan.py
import yaml
def load_yaml(path):
f= open("beifan.yaml",encoding="utf-8")#打开文件
s=f.read()#读取文件内容
data=yaml.safe_load(s)
return data
1.设计用例内容
1.名字:请求首页数据接口
2.标记【可选】
3.步骤
3.1请求接口:GET https://www.baidu.com
3.2响应断言: status_code ==200
3.3提取变量: json()[‘code’]
yaml用例
test_api
name:登录成功的测试用例
steps:
- request:#发生请求
method:POST
url:http:///116.62.63.211/shop/api.php?application=app&application_client_type=wexin
params:
s:user/login
json:{
"accounts":"beifan_1105",
"pwd":"beifan_1105",
"type":"username"
}
-reponse:#断言响应
status_code:200
json:
code:0
msg:登录成功
data:
username:beifan_1105
-extract:#提取变量
token:[json,$.data.token]
1.请求接口
外部工具:requests
HTTP协议抓包角度,请求由三部分组成:
行:方法+地址(必填)
头:请求头(键值对)
体:参数内容
request.py
import requests
###1.行
url='http:///116.62.63.211/shop.api.php'
#GET 方法
request.get(url)
#post方法
requests.post(url)
#任意方法
requests.request('MOVE',url)
###2.头
method='post'
url='http://116.62.63.211/shop/api.php'
requests.request(
method,url,
headers={#字符串字典
"1":"a",
"2":"b",
}
)
###3.参数
method='post'
url='http://116.62.63.211/shop/api.php'
requests.request(
method,url,
json={#任意内容字典
"a":1,
"b":[1,2,3],
"c":{},
}
)
断言响应
1.响应里有什么
2.响应如何断言
从http协议抓包的角度,响应由三个部分构成
行:状态码
头:响应头(键值对)
体:响应内容
#resp 就是响应
#获取响应的内容
print(resp.status_code)#状态码
print(resp.headers)#响应头
print(resp.text)#响应正文
print(resp.json())#响应正文转成json
#断言单个内容是否正确
assert resp.station_code==200
assert '美酒' in resp.text
assert resp.json()['data']['banner_list'][0]["name"]=="美德"
#断言全部内容
from responses_validator import validator
validator(
resp,
status_code=200,
text="*美酒*",
json={
"data":{
"banner_list":[{"name":"美酒"}]
}
}
)

3.变量提取
基本原则:
案例:封装到extract_utills.py
import jsonpath
#['data']['banner_list'][0]["name"]
def extract(resp,attr_name,exp):
try:
resp.json=resp.json()
except Exception:
resp.json={}
attr=getattr(resp,attr_name)
res=jsonpath.jsonpath(attr,exp)
return res[0]
在request.py导入
from extract_utills import extract
var=extract(resp,'json','$..banner_list[0].name')
print(var)
4.框架落地封装

入口文件main.py
import os
import pytest
pytest.main()
os.system(f"allure generate -c -o report temps")
配置文件pytest.ini

tests文件夹
包含test_api.yaml和test_yaml.py
其中test_api.yaml
name:登录成功的测试用例
steps:
- request:#发生请求
method:POST
url:http:///116.62.63.211/shop/api.php?application=app&application_client_type=wexin
params:
s:user/login
json:{
"accounts":"beifan_1105",
"pwd":"beifan_1105",
"type":"username"
}
-reponse:#断言响应
status_code:200
json:
code:0
msg:登录成功
data:
username:beifan_1105
-extract:#提取变量
token:[json,$.data.token]
其中test_yaml.py
import allure
from commons.runner_utills import runner
from commons.yaml_utills import load_yaml
def test_yaml():
my_var={}
data=load_yaml("tests/test_api.yaml")
allure.title(data['name'])
for step in data['steps']:
for k,v in step.items():
runner(k,v,my_var)
commons文件
extract_utills.py
import jsonpath
#['data']['banner_list'][0]["name"]
def extract(resp,attr_name,exp):
try:
resp.json=resp.json()
except Exception:
resp.json={}
attr=getattr(resp,attr_name)
res=jsonpath.jsonpath(attr,exp)
return res[0]
runner_utils.py
import requests
import responses_validator
from commons.extrat_utills import extract
import logging
logger=logging.getLogger("beifan")
def runner(k,v,my_var):
match k:
case 'request' :#发送请求
logger.info('1.正在发送请求。。。')
logger.info(f'{v}')
my_var['resp']=requests.request(**v)#字典使用两颗星
case 'reponse' :#断言响应
logger.info('2.正在响应断言。。。')
logger.info(f'{v}')
responses_validator.validator(my_var['resp'],**v)#字典使用两颗星
case 'extract' :#变量提取
logger.info('3.正在提取变量。。。')
for var_name,var_exp in v.items():
value=extract(my_var['resp'],*var_exp)#列表使用一个星
logger.info(f"{var_name} ={value}")
#查询数据库。。。
进一步完善
1.YAML用例测试文件上传?
2.YAML用例进行数据去上传?
3.YAML用例进行自定义断言?
4.YAML用例进行数据库查询?
两个方向:1.自动化工程师2.测试开发工程师
终端安装
pip pytest检验
pytest
创建test_beifan.py
#1.创建test_开头的文件
#2.创建test_开头的函数
def test_abc():
assert False
#3.在函数中使用断言 IF assert
#4.用例可以放在类中,类名字必须Test开头
class TestBeiFan:
def test_abc(self):#实例方法的绑定参数self
assert False
#5.用例文件可以位于任一目录,一般为test目录
方法一:可以点击执行箭头执行
方法二:用mark筛选
创建test_1_beifan.py,目前加上上个文件有四个用例,对要执行的用例进行mark装饰
#1.创建test_开头的文件
#2.创建test_开头的函数
def test_abc():
assert False
#3.在函数中使用断言 IF assert
#4.用例可以放在类中,类名字必须Test开头
class TestBeiFan:
#要执行的用例mark装饰
@pytest.mark.first
def test_abc(self):#实例方法的绑定参数self
assert False
#5.用例文件可以位于任一目录,一般为test目录
在终端输入
pytest -m first执行了标记的用例
Q:但是如果标记多个要怎么去使用更便捷呢?
创建配置文件pytest.ini
步骤:注册–>使用–>筛选
[pytest]
markers =
aaa
bbb
ccc
first
使用的时候在用例在装饰器就好了,如:
pytest.mark.first
执行直接在终端输入
pytest -m first
Q:mark的高级用法?不需要注册的用法
内置标记,特性:不需要注册,不仅用例筛选还有特殊效果
@pytest.mark.skip无条件跳过
@pytest.mark.xfail预期失败
@pytest.mark.parametrize("n",[1,2,3])改变用例执行数量和结果
其他:
-skipif 有条件跳过
-usefixture 自动使用fixture
-parameterize 参数化测试:动态的生成用例
定义:
fixture:夹具,实现用例前置,后置的自动化操作,其中yield是生成器的意思,fixture是一个函数
原理:
def beifan():
print(1)
yield
print(2)
g=beifan()
print('执行前置')
next(g)
print('执行用例')
print('执行后置')
next(g)
执行效果:

Q:怎么使用fixture?
步骤:添加装饰器
@pytest.fixture–>使用yield区分前后置
import pytest
#1.创建fixture
@pytest.fixture
def beifan()
print('用例前置操作:连接数据库,启动浏览器,生成测试数据')
yield
print('用例后置操作:关闭数据库,退出浏览器,生成测试数据')
#2.使用名为befan的fixture
@pytest.mark.fixture
def test_abc(beifan):
print('loading test add')
assert 1 + 1 == 3
#注意用例执行失败后面代码不会执行但会执行后置
print('test ending')
执行效果:

Q:两个用例使用一个fixture这么设计代码?以防重复使用fixture
设置作用域,共享fixture。
优点:复用代码减少前置和后置的操作耗时。在用例之间传递数据。
缺点:在共享的范围内容,fixture不会重复执行
fixture的作用范围:
function ,默认,在函数的范围,进行共享class ,在同一类的范围,进行共享moudle,在同一模块(文件)范围,进行共享package,在同一包(目录)的范围,进行共享session,在同一此执行用例所有用例,进行共享
import pytest
#1.创建fixture,fixture的作用范围
#这里session和modle都可以
@pytest.fixture(scope='session')
def beifan()
print('用例前置操作:连接数据库,启动浏览器,生成测试数据')
yield
print('用例后置操作:关闭数据库,退出浏览器,生成测试数据')
#2.使用名为befan的fixture
@pytest.mark.fixture
def test_abc(beifan):
print('loading test add')
assert 1 + 1 == 3
#注意用例执行失败后面代码不会执行但会执行后置
print('test ending')
#2.使用名为befan的fixture
@pytest.mark.fixture
def test_def(beifan):
print('loading test add')
assert 1 + 1 == 3
#注意用例执行失败后面代码不会执行但会执行后置
print('test ending')
Q:fixture在用例之间传递数据的用法
import pytest
#1.创建fixture,fixture的作用范围
@pytest.fixture(scope='session')
def beifan()
print('用例前置操作:连接数据库,启动浏览器,生成测试数据')
yield
print('用例后置操作:关闭数据库,退出浏览器,生成测试数据')
#2.使用名为befan的fixture
#使用命名空间,字典的形式,断言之前
@pytest.mark.fixture
def test_abc(beifan,doctest_namespace):
#定义name
name='beifan。。。'
doctest_namespcae['name']=name
print('loading test add')
assert 1 + 1 == 3
#注意用例执行失败后面代码不会执行但会执行后置
print('test ending')
#2.使用名为befan的fixture
@pytest.mark.fixture
def test_def(beifan,doctest_namespace):
#取出name
name=doctest_namespace['name']
print(name)
print('loading test add')
assert 1 + 1 == 3
#注意用例执行失败后面代码不会执行但会执行后置
print('test ending')
单元测试框架定义:针对软件开发最小的单元(函数,方法)进行正确性位置测试
单元测试框架:java(junit,testing)python(unittest,pytest)
单元测试框架的作用
测试发现:从多个文件中找到测试用例测试执行:按照一定的顺序和规则去执行并且生成结果测试判断:通过断言判断预期结果和实际结果的差异测试报告:统计测试的结果,耗时,通过率等生成美观的测试报告自动化框架的内容:单元测试框架,pom设计模式,数据驱动,关键字驱动,全局配置文件的封装,日志监控,selenium和request二次封装,断言,报告邮件,更多…
如下:建立requirements.txt 内容如下
pytest 本身
pytest-html 生成html格式化报告
pytest-xdist 测试用例分布式进行,多CPU分发
pytest-ordering 用于改变测试用例的执行顺序
pytest-rerunfailures 用例失败后重跑
allure-pytest 用于生成美观的测试报告
终端上输入安装:
pip install -r requirements.txt
或者
pip pytest
检验:
pytest --version
或
pytest
模块命名
以test_开头或者 _test结尾测试类必须以Test开头并且不再有ini方法测试方法必须以test开头1.主函数运行方式
(1)运行所有:pytest.main()
class TestLogin:
def test_01_login(self):
print("测试登录")
if __name__ == '__main__':
pytest.main()
(2)运行指定模块:
pytest.main(['-vs','test_login.py'])
(3)指定目录:
pytest.main(['-vs','./test_other.py'])
(4)通过nodeid指定用例运行:nodeid由模块名,分隔符,类名,函数名组成
pytest.main([’-vs‘,’./interface_testcase/test_interface.py::test_04_func‘])
2.命令行模式
(1)运行所有:
pytest
(2)指定模块:
pytest -vs test_login.py
(3)指定目录:
pytest -vs ./test_other.py
(4)指定目录:
pytest -vs ./interface_testcase/test_interface.py::test_04_func
3.参数详解
-v 输出更详细的信息
-s 输出调试信息
-vs:这两个参数一起用
-n:支持多线程或者分布式运行测试用例
如:pytest -vs ./test_login.py -n 2
–returns NUM:失败用例重跑
-x:表示只要要一个用例报错,那么测试就停止
–maxfall=2 出现两个用例失败就停止
-k:根据测试用例的部分字符串指定测试用例
如:pytest -vs/testcase -k"ao"
class TestLogin:
def test_01_login(self):
print("测试登录")
if __name__ == '__main__':
pytest.main(['-s'])
案例:创建一个all.py文件运行指定的test用例和控制输出的信息
import pytest
if __name__ == '__main__':
pytest.main(['-vs','test_login.py'])
相当于终端输入
pytest -vs test_login.py
3.结合pytest.ini全局配置文件执行
pytest为整个pytest的核心配置文件
位置:一般放在项目的根目录
编码:必须是ANSI编码格式,可以使用notepad++修改编码格式
作用:改变pytest默认行为
运行规则:不管是主函数的模式运行,命令行模式运行,都会去读取ini文件
1 [pytest]
2 #命令行参数
3 #常见:--html=./reports/report.html --reruns 2
4 addopts = -vs -m "user_manager or smoke"
5 #配置执行的用例位置
6 testpaths = ./testcases
7 #配置修改默认的模块规则
8 python_files = test_*.py
9 #配置修改默认的类规则
10 python_classes = Test*
11 #配置修改默认的用例规则
12 python_functions = test_*
13 #配置基础路径
14 base_url = http://www.baidu.com
15 #标记
16 markers =
17 smoke:冒烟测试用例
18 product_manage:商品管理
19 user_manager:用户管理
区分
unittest:ascII的大小来绝对的执行顺序
pytest:默认从上到下
改变默认的执行顺序,使用mark标记
添加注释,其中1是执行的顺序
@pytest.mark.run(order=1)
smoke:冒烟用例,分布在各个模块内
添加冒烟标记在.py文件
@pytest.mark.smoke
注意要在pytest.ini文件打一下标记
#标记
markers =
smoke:冒烟测试用例
product_manage:商品管理
user_manager:用户管理
在终端输入
pytest -vs -m "smoke"
想执行其他模块
pytest -vs -m "smoke and 其他模块的名称"
无条件跳过
@pytest.mark,skipif(reason=’跳过原因‘)
有条件跳过
@pytest.mark,skipif(条件,reason=’跳过原因‘)
例如
@pytest.mark.skipif(age>=18,reason='已成年')
前后置:setup_class、setup、teardown、teardown_class
class TestPosition:
def setUp_class(self):
print('
在每个类执行前的准备工作,例如:创建日志对象、数据库链接、接口请求对象')
def setup(self):
print('
执行用例之前的开始动作,比如打开浏览器')
def test_01(self):
print('
测试用例')
def teardown(self):
print('
执行用例之前的扫尾动作,比如关闭浏览器')
def teardown_class(self):
print('
在每个类执行前的准备工作,例如:销毁日志对象、数据库链接、接口请求对象')
装饰器:
@pytest.fixtrue(scope=“作用范围”,autouse=“自动执行”,params=“参数化”,ids=“参数别
名”,name=“固件别名”)
案例:
@pytest.fixture(scope="",params="", autouse="",ids="",name="")
def my_fixture():
print('这是前置的方法,可以实现部分以及全部用例的前置')
yield
print('这是后置方法,可以实现部分以及全部用例的后置')
def test_02(self,my_fixture):
print('
执行fixture')
用法:
scope表示的是被@pytest.fixture标记的方法的作用域。function(默认),class,moudle,package/sessionparams:参数化(支持,列表[],元组(),字典列表[{},{},{}],字典元组({},{},{})
import pytest
@pytest.fixture(scope='function',params=['参数1','参数2','参数3'])
def my_fixture(request):
print('前置')
yield request.param
# 注意yield和return都表示返回的以上,但是return后面不能跟代码,yield可以
print('后置')
def test_02(self,my_fixture):
print('
执行fixture')
注意:params和yield不能一起用但是可以和前置一起用。
autouse=True:自动化使用,默认False
ids:当使用params参数化时,给每一个值设置一个变量名,意义不大
name:给表示的是被@pytest.fixture标记的方法取一个别名
当取了别名之后,那么原来的名就用不了了
conftest.py文件是单独存放的一个夹具的配置文件,名称不能更改
用处可以在不同的py文件中使用同一个fixture函数
原则上conftest.py需要和运行的用例放在统一层,并且不需要做任何的import导入的操作
案例
全局拥有全局前置,各个子模块下面有一个前置,对于用例编写为
def test_01_testconftest(self,all_fixtuture,user_fixture)
print("test用例")
print(user_fixture)
print(all_fixture)
这样全局前置包含了子模块的前置
setup/teardown,setup_class/teardown_class 它是作用于所有用例或者所有的类
@pytest.fixture() 它的作用是局部的也是全局的
conftest.py和@pytest_fixture()结合使用,作用于全局的前后置
assert
以前是使用的是pytest_html生成,现在可以使用allure+pytest结合生成美观的报告
1.下载、解压、配置path路径
https://github.com/allure-framework/allure2/releases
配置环境变量如:
D:allure-2.30.0in
验证
allure --version
2.加入命令到pytest.ini文件,生成json格式的临时报告
addopts = -vs --alluredir ./temp
3.生成allure报告
allure generate 命令,固定的
./temp 临时的json格式的报告
-o 输出output
./report 生成allure报告路径
.clear 清空./report路径原来的报告
if __name__ == '__main__':
time.sleep(3)
# pytest.main(['-vs','test_login.py'])
pytest.main()
# allure generate 命令,固定的;./temp 临时的json格式的报告 -o 输出output ./report 生成allure报告路径 .clear 清空./report路径原来的报告
os.system('allure generate ./temp -o ./report --clear')
1,断言的封装
2,allure报告的定制
3,关键字驱动和数据驱动结合实现接口自动化测试
4,python的反射
正常:先初始化对象,再调方法
反射:通过对象得到类对象,然后通过类对象调用方法
5,jenkins的持续集成和allure报告的集成,并且根据项目的自动化的报告错误率发送电子邮件
发展背景:
目前市面上主流的接口测试工具:(适用于中小型项目)
postman+newman+git+jenkinsjmeter+ant+git+jenkins目前主流的接口自动化测试技术:(适用于大小型的项目)
安装
requests用来发送http请求以及接受http响应的python第三方库,主要用于接口自动化测试
pip install requests
requests库常用的方法
requests.get() url是接口的地址,param用于传参
requests.post() url是接口地址,data用于传参,json也适用传参
data和json传参的区别:主要是通过请求头Content-Type来区分
Content-Type:作用是服务器要求传入的报文的内容类型
请求:请求方式,请求路径,请求头,请求正文
requests.pull()
requests.delete()
requests.request() 可以发送所有类型的请求:get,post,put,delete
拓展:postman的四种传参的方式对应的Content-Type的值如下:
from-data:Content-Type:multipart/form-data、boundary=
x-www-from-urlencoded:Content-Type:applic.ation/x-www-form-urlencoded
raw:
text:Content-Type:text/plain
javascript:Content-Type:application/javascript
json:Content-Type:application/json
html:Content-Type:application/xml
binary:Content-Type:application/binary
data和json传参以及Content-Type的关系如下:
1.data传参:
data传参:报文时dict类型,那么默认Content-Type application/x-www-form-urlencoded
data传参:报文是str类型,那么默认Content-Type:text/plain
2.json传参:
报文可以是dict类型,那么默认Content-Type application/json
注意:
json是字典类型的字符串显示。
json.loads() 把json字符串转化为dict格式,也可以传str格式(如果是嵌套字典那么就需要使用json.dumps()把嵌套字典转化为json字符串)
json.dumps() 把dict格式转换为json字符串(包括嵌套的dict)
不管是get,post,put,delete,都是调用的requests.request方法。而requests.reques方法调用的是session.reques方法
method 请求方式
url 请求路径
params=None get方式传参
data=None post方式传参
json=None post方式传参
headers=None 请求头
cookie=None 请求cookie
files=None 文件上传
<input type="hidden" name="csrf_token" value="e7202bf558a7eb34"/>
90%以上的基于web的接口都有cookie鉴权
两种解决方式:
使用cookie关联
案例注释:接口关联变量,在第一个接口取值,在第二个接口传值,第三个接口文件上传,访问首页的接口,解决鉴权
import requests
class TestRequest:
#全局变量,类变量,通过类名调用
access_token=""
csrf_token=""
php_cookie=""
def test_get_token(self):
url:"https://api.weixin.qq.com/cgi-bin/token"
data:{
"grant_type":"client_credential",
"appid":"wx74a8627810cfa300",
"secret":"e40a02f9cf9d79df497e6aaf93ab80"
}
res=requests.request(method="get",url=url,params=data)
print(res.json())
TestRequest access_token = res.json()['access_token']
# post请求:编辑标签接口
def test_edit_flag(self):
url:"https://api.weixin.qq.com/cgi-bin/tags/updat?access token="+TestRequest.access_token
data=[
"tag":
{
"id":134,
"name":"广东人"
}
]
str_data=json.dumps(data)
res=requests.request(method="post",url=url,data=str_data)
print(res.json())
# 文件上传
def test_file_upload(self):
url:"https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token="+TestRequest.access_token"+TestRequest.access_token
data={
"media":open(r"E:shu.png","rb")
}
res=requests.request(method="post",url=url,files=data)
print(res.json())
# 访问首页的接口
def test_start(self):
url:"https://47.107.116.139/phpwind"
res=requests.request(method="get",url=url)
print(res.text)
# 正则提取token
obj=re.search('name="csrf_token" value="(.*?)"',res.text)
# print(obj.group(1))
TestRequest.csrf_token=obj.group(1)
# 解决鉴权的方式一:提取cookie
TestRequest.php_cookie=res.cookies
# 登录接口
def test_login(self):
url:"https://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
data={
"username":"msxy",
"password":"msxy",
"csrf_token":TestRequest.csrf_token,
"backurl":"http://47.107.116.139/phpwind/",
"invite":""
}
# 请求头
headers={
"Accept":"application/json, text/javascript, /; q=0.01",
"X-Requested-with":"XMLHttpRequest"
}
res=requests.request(method="post",url=url,data=data,headers=headers,cookies=TestRequest.php_cookie)
print(res.json())
# 解决鉴权的方式一:提取cookie
TestRequest.php_cookie=res.cookies
if __name__ == '__main__':
TestRequest().test_get_token()
TestRequest().test_edit_flag()
TestRequest().test_file_upload()
TestRequest().test_start()
TestRequest().test_login()
使用session关联
import requests
class TestRequest:
#全局变量,类变量,通过类名调用
access_token=""
csrf_token=""
# php_cookie=""
sess=requests.session()
def test_get_token(self):
url:"https://api.weixin.qq.com/cgi-bin/token"
data:{
"grant_type":"client_credential",
"appid":"wx74a8627810cfa300",
"secret":"e40a02f9cf9d79df497e6aaf93ab80"
}
res=requests.request(method="get",url=url,params=data)
print(res.json())
TestRequest access_token = res.json()['access_token']
# post请求:编辑标签接口
def test_edit_flag(self):
url:"https://api.weixin.qq.com/cgi-bin/tags/updat?access token="+TestRequest.access_token
data=[
"tag":
{
"id":134,
"name":"广东人"
}
]
str_data=json.dumps(data)
res=requests.request(method="post",url=url,data=str_data)
print(res.json())
# 文件上传
def test_file_upload(self):
url:"https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token="+TestRequest.access_token"+TestRequest.access_token
data={
"media":open(r"E:shu.png","rb")
}
res=requests.request(method="post",url=url,files=data)
print(res.json())
# 访问首页的接口
def test_start(self):
url:"https://47.107.116.139/phpwind"
res=TestRequest.sess.request(method="get",url=url)
# print(res.text)
# 正则提取token
obj=re.search('name="csrf_token" value="(.*?)"',res.text)
# print(obj.group(1))
TestRequest.csrf_token=obj.group(1)
# 登录接口
def test_login(self):
url:"https://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
data={
"username":"msxy",
"password":"msxy",
"csrf_token":TestRequest.csrf_token,
"backurl":"http://47.107.116.139/phpwind/",
"invite":""
}
# 请求头
headers={
"Accept":"application/json, text/javascript, /; q=0.01",
"X-Requested-with":"XMLHttpRequest"
}
res=TestRequest.sess.request(method="post",url=url,data=data,headers=headers)
print(res.json())
if __name__ == '__main__':
TestRequest().test_get_token()
TestRequest().test_edit_flag()
TestRequest().test_file_upload()
TestRequest().test_start()
TestRequest().test_login()
目标:python+requests+pytest+allure+jenkins
策略:去掉全局变量,用yaml文件代替保存下载pyyaml的包(搜搜)
import os
import yaml
# 读取
def read_yaml():
with open(os.getcwd()+'/extract.yaml',mode='r',encoding='utf-8') as f:
value = yaml.load(stream=f, Loader=yaml.FullLoader)
return value
# 写入
def write_yaml(data):
with open(os.getcwd()+'/extract.yaml', mode='a',encoding='utf-8') as f:
yaml.dump(data, stream=f,allow_unicode=True)
# 清空
def clear_yaml(data):
with open(os.getcwd()+'/extract.yaml', mode='w',encoding='utf-8') as f:
f.truncate()
如果两个测试用例要使用同一个session怎么办
通过同一个session发送请求
方便统计用例。日志封装
import requests
# 关于接口自动化框架之统一请求封装
class RequestUtil:
# 全局变量 类变量 通过类名调用
sess=requests.session()
def send_request(self, url, method, datas=None, **kwargs):
method=str(method).lower()
res=None
if method=='get':
res=RequestUtil.sess.request(method=method,url=url,params=datas,**kwargs)
elif method=='post':
if datas and isinstance(datas,dict):
datas=json.dumps(datas)
res=RequestUtil.sess.request(method=method,url=url,datas=datas,**kwargs)
else:
pass
return res
步骤
1.将接口的数据的格式按照这种方式书写:
-
name:
request:
method: get
url: https://api.weixin.qq.com/cgi-bin/token
data:
grant_type: client_crediential
appid: wx6b11b3efd1cdc290
secret: 106a9c61571dfdf55f6255cdgs5622
validate: None
2.在yaml_util新增一个读取测试用例的方法
# 读取测试用例的方法
def read_testcase(yaml_name):
with open(os.getcwd()+'/testcases/'+yaml_case,mode='r',encoding='utf-8') as f:
value = yaml.load(stream=f, Loader=yaml.FullLoader)
return value
3.在test_api.py 通过装饰器读取字典
如:
@pytest.mark.smoke
@pytest.mark.parametrize("args_name",read_testcase('get_token.yaml'))
def test_get_token(self,args_name):
url=args_name['request']['url']
data=args_name['request']['data']
method=args_name['request']['method']
res=RequestUtil().send_request(method=method,url=url,datas=data )
¥24.90
21天攻克小学语文阅读核心考点 赠视频课6-12岁儿童小学生通用阅读提分技巧创意公式法三四五六年级阅读理解训练题人教版教辅书籍
¥60.00
老鼠记者中文全球版全套90册 新版第一至十六辑校园侦探推理冒险小说读物小学生三四五六年级课外书籍8-14岁青少年阅读漫画俏鼠
¥168.00
六一儿童节玩具女孩2生日礼物4一5小孩子7女生3到6岁女童三益智61
¥19.90
儿童三面牙刷牙膏刷牙宝宝软毛0到3到6一12岁以上小孩换牙期u型3d
¥29.90
运动内衣跑步防震发育期初中生青春期高中生15岁二三阶段少女文胸
¥19.80
睡前故事儿童绘本0-2-5-8到4岁绘本阅读幼儿园老师推荐3一6幼儿早教启蒙读物小中班宝宝睡前10分钟故事书籍绘本3–6岁三岁孩子图书