1.框架结构
-
common: 这是一个package,主要用来存储所有的底层代码封装
-
logs: 这是一个目录,主要用来存放日志文件
-
report: 这是一个目录,里边的data表示测试结果数据,里边的html表示测试报告,注意这两个目录都是每次执行测试时自动生成的
-
testcases: 这是一个目录,主要用来存储excel文件,excel文件里是接口测试的相关数据
-
conftest.py: 重写pytest自带的一个内置函数的,统一管理自定义fixture的
-
pytest.ini: pytest相关的配置参数
-
run.py: 是整个框架执行的入口
按照一定的维度进行分类,每个分类可以当做一个sheet工作表
-
全局变量
主要用来管理我们的公共数据
-
接口默认参数
通常在一个项目中,参数如果很多的时候,我们针对测试用例去传递数据就会很麻烦,所以我们针对每个接口的默认参数数据进行单独管理,在测试时只需要针对当前测试用例传递你要测试的某个字段值即可,其他字段统统来自于默认参数
填写参数的规则:对于接口参数可能会有多种类型,表单的,查询的,json的,文件的等等
表单类型时:
{
"data":{
"xxx":"xxjsdhdh"
}
}
查询参数:
{
"params":{
"xxx":"xxjsdhdh"
}
}
json参数:
{
"json":{
"xxx":"xxjsdhdh"
}
}
混合参数,比如既有表单又有查询:
{
"params":{
"xxx":"xxjsdhdh"
},
"data":{
"ddd":"ddff"
}
}
-
测试集合管理
测试集合管理 主要是为了控制要执行哪些测试集合,以及测试集合执行的顺序
测试集合名称:对应的就是某个测试集合的sheet工作表名称
是否执行:只有值是y时才会被执行,其他值不会被执行
- 测试集合
每个测试集合在excel里是一个单独的sheet工作表,他负责某个模块或者某个接口相关的测试用例数据管理,一个测试集合中是可以存在多个测试用例的
-
序号:仅仅只是个标识,没啥作用
-
用例名称:一个用例可能会有多个接口的先后调用,在excel里一行数据就是针对一个接口的调用,多行数据就是多个接口的调用,如果一个用例需要用多行数据,那么这几行的用例名称保持一致
-
接口名称:该列主要是为了和接口默认参数中的接口名称进行关联,通过接口名称得到该接口对应的默认参数,然后再根据测试数据来决定参数是什么
-
接口地址:表示接口地址,在接口地址里域名几乎都是相同的,或者说是公共的,所以我们将域名作为了公共变量进行存储,那么在这里需要调用公共变量域名,调用方式${host},host就是公共变量中的一个变量
-
请求方式:get/post/put/delete
-
接口头信息:指的就是headers,对于一个接口来说不一定有特殊的头信息,那么就不填,如果有需要按照如下格式进行填写,以json格式字符串的方式:
-
token是要从登录接口的返回值中提取的,提取之后保存到一个变量中,咱们这里保存的变量名称叫做token,所以在这里引用了变量token,引用方式就是${token}
{
"Admin-Token":"${token}"
}
假如特殊的头信息有多个,写法就是在json字符串中继续追加键值对,比如:
{
"Admin-Token":"${token}",
"Content-Type":"application/json"
}
-
测试数据:指的是在接口发起调用时传递的你要测试的某个数据,对于一个接口来说参数有很多,但是我们每次测试时,可能只是针对一两个参数进行测试,可以借助之前所学的通过jsonpath去匹配某些参数,并且替换他们的值
设计思路:
一个json格式的字符串,其中参数类型分为data、json、params、files
其中的key是你要替换的目标参数对应的jsonpath,value就是该参数对应的新值,也就是测试数据
{
"json":{
"$.entity.customer_id":999999999,
}
}
如果要替换多个参数:
{
"json":{
"$.entity.name":"自动化${{cur_timestamp()}}",
"$.entity.num":"${timestamp}}"
}
}
- 响应提取:响应提取是为了从当前接口的返回值中提取某些信息,保存在变量中,以便后续接口要使用时进行变量引用,这也是我们常说的关联
比如每个接口都要用到token,token是登录接口产生的,所以登录的响应提取里要写提取内容,规则是以json格式的字符串作为标准格式,其中key是要保存的变量名称,value是要提取的参数对应的jsonpath表达式
{
"token":"$.Admin-Token"
}
期望响应状态码:http响应状态码期望值
期望响应信息:表示我们要针对接口的响应信息中的某些参数进行断言,设计规则如下:
依然是json格式的字符串,最外层是一个列表,里边套的是多个字典,一个字典就是一个参数的断言。
每个字典的格式是必须包括两个键值对,一个actual表示实际值的key,实际值的value是参数对应的jsonpath表达式,一个expect表示期望值的key,期望值的value是期望内容
[
{
"actual":"$.code",
"expect":500,
},
{
"actual":"$.msg",
"expect":"产品编号已存在,请校对后再添加!",
}
]
2.数据驱动框架底层代码实现
1.创建项目 依赖于设计去创建项目结构
2.excel数据读取 在common这个package下创建一个python文件,叫做testcase_util.py
# !/usr/bin python3
# encoding: utf-8 -*-
# @Copyright:
import openpyxl
# 读取全局变量sheet工作表
def get_variables(wb):
sheet_data = wb['全局变量']
variables = {} # 用来存储读到的变量,名称是key,值是value
lines_count = sheet_data.max_row # 获取总行数
for l in range(2,lines_count+1):
key = sheet_data.cell(l,1).value
value = sheet_data.cell(l,2).value
variables[key] = value
return variables
def get_api_default_params(wb):
sheet_data = wb['接口默认参数']
api_default_params = {} # 用来存储读到的变量,名称是key,值是value
lines_count = sheet_data.max_row # 获取总行数
for l in range(2,lines_count+1):
key = sheet_data.cell(l,1).value
value = sheet_data.cell(l,2).value
api_default_params[key] = value
return api_default_params
# 获取要执行的测试集合名称
def get_casesuitename(wb):
sheet_data = wb['测试集合管理']
lines_count = sheet_data.max_row # 获取总行数
cases_suite_name = [] # 用来存储要执行的测试集合名称
for l in range(2,lines_count+1):
flag = sheet_data.cell(l,2).value
if flag == 'y':
suite_name = sheet_data.cell(l,1).value
cases_suite_name.append(suite_name)
return cases_suite_name
# 需要根据要执行的测试集合名称来读取对应的测试用例数据
def read_testcases(wb,suite_name):
sheet_data = wb[suite_name]
lines_count = sheet_data.max_row # 获取总行数
cols_count = sheet_data.max_column # 获取总列数
"""
规定读出来的测试数据存储结构如下:
{
“新增客户正确”:[
['apiname','接口地址','请求方式','头信息',....],
['apiname','接口地址','请求方式','头信息',....],
],
"新增客户失败-用户名为空":[
['apiname','接口地址','请求方式','头信息',....]
],
"新增客户失败-手机号格式不正确":[
['apiname','接口地址','请求方式','头信息',....]
]
}
"""
cases_info = {} #用来存储当前测试集合中的所有用例信息的
for l in range(2,lines_count+1):
case_name = sheet_data.cell(l,2).value # 测试用例名称
lines = [] # 用来存储当前行测试数据的
for c in range(3,cols_count+1):
cell = sheet_data.cell(l,c).value # 当前单元格数据
if cell == None: # 处理空单元格
cell = ''
lines.append(cell)
# 判断当前用例名称是否已存在于cases_info中
# 如果不存在,那就是直接赋值
# 否则就是在原来的基础上追加
if case_name not in cases_info:
cases_info[case_name] = [lines]
else:
cases_info[case_name].append(lines)
return cases_info
# 整合所有要执行的测试用例数据,将其转成pytest参数化需要的数据结构格式
def get_all_testcases(wb):
"""
整合后的数据结构是
[
['新增客户接口测试集合','新增客户正确',[[],[]]],
['新增客户接口测试集合','新增客户失败-用户名为空',[[],[]]],
['新增客户接口测试集合','新增客户失败-手机号格式不正确',[[],[]]],
['新建产品接口测试集合','新建产品正确',[[],[]]],
['新建产品接口测试集合','新建产品失败-产品编码重复',[[],[]]],
]
:param wb:
:return:
"""
test_data = [] # 用来存储所有测试数据
# 获取所有要执行的测试集合名称
cases_suite_name = get_casesuitename(wb)
for suite_name in cases_suite_name:
# 遍历读取每个要执行的测试集合sheet工作表中的测试用例数据
cur_cases_info = read_testcases(wb,suite_name) # 是个字典
for key,value in cur_cases_info.items():
# key实际上就是测试用例名称,value实际上测试用例多行数据信息
case_info = [suite_name,key,value]
test_data.append(case_info)
return test_data
if __name__ == '__main__':
wb = openpyxl.load_workbook('../testcases/CRM系统接口测试用例.xlsx')
# print(get_variables(wb))
# print(get_api_default_params(wb))
# print(get_casesuitename(wb))
# print(read_testcases(wb,'新增客户接口测试集合'))
print(get_all_testcases(wb))
3.接口调用底层方法封装
在common目录下创建一个client.py,写上如下代码
# !/usr/bin python3
# encoding: utf-8 -*-
import jsonpath
import requests
session = requests.session()
class RequestsClient:
def send(self,url,method,**kwargs):
try:
self.resp = session.request(url=url,method=method,**kwargs)
except BaseException as e:
raise BaseException(f'接口发起异常:{e}')
return self.resp
# 针对jsonpath的数据提取封装一个方法
# 第一个参数指的是你要匹配的数据的jsonpath表达式
# 第二个指的是你想返回匹配到的第几个,默认是0返回第一个
def extract_resp(self,json_path,index=0):
# 注意有的接口是没有返回信息的,返回信息是空的
text = self.resp.text # 获取返回信息的字符串形式
if text != '':
resp_json = self.resp.json() # 获取响应信息的json格式
# 如果能匹配到值,那么res就是个列表
# 如果匹配不到res就是个False
res = jsonpath.jsonpath(resp_json,json_path)
if res:
if index {md5(123456)}} 再比如
{rdm_future_date_time(+60d)}}{{var_name}}}'
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?