对于接口测试,一开始都是一个个接口用例写过去,但写久了就觉得效率有些慢了。
在将接口区分为关联接口和非关联接口后,就好多了。
区分接口
接口虽然都有参数要求,但对参数入手,可以区分为关联接口与非关联接口,关联接口需要从其他接口的返回中获取某个参数值;而非关联接口却只需要提供固定的参数或请求头即可。以下主要是对非关联接口的快速测试思路。
一般对于固定参数的非关联接口,或者只需要从某个接口获取特定的关联参数,就可以在后续多个接口的测试中使用。对于这样的接口,可以做成数据驱动的方式来减少对接口用例代码的重复工作。
数据驱动
接触了一段时间接口自动化后应该都能知道数据驱动这个东西,一个接口不可能只有一个测试用例或者说一组测试数据,需要覆盖正常与非正常的情况。而数据驱动能很好的解决这种多测试数据的场景。
而在对接口自动化代码编写一段时间后,就在想是否能直接通过一个通用的测试代码或测试方法,来对所有的接口进行测试。
在对接口进行区分后,可以发现非关联的接口,其接口参数基本固定,只需要在已有的数据驱动中添加好条件判断,就可以实现对这部分接口的测试。虽然与关联接口并不通用,或者说是关联接口通过数据驱动来进行测试会更复杂,但也能减少很大一部分的用例代码。
变量数据驱动
直接在代码文件或模块中定义变量作为数据驱动,对于关联用例,我一般是通过这种方式获取其他接口的返回值以实现接口数据关联调用。
维护问题见仁见智,在有充足的注释说明下,一边都好维护。
json数据驱动
一般比较建议区分保存为多个json文件,不建议一个json文件中包含太多的内容,受限于json格式,内容显示要么是一长串,要么就是json标准格式,占用较多空间,虽然比变量数据驱动的维护要好些,但对于系统来说,大量的接口数据定然导致文件内容过厂。
json数据文件
{
"topic_get_param":[
[
{
"mdrender": "false",
"accesstoken": "8b77c60e-b817-4f0d-94bc-59ddf939e1ad"
},200,true],
[
{
"mdrender": "false",
"accesstoken": "eacb8ae1-9694-458b-9148-4e65d923399e"
},200,true]
]
}
json数据驱动调用
def get_json_data(filename,target1=None,target2=None,target3=None,target4=None):
json_data = json.load(open(filename, mode='r', encoding='utf8'))
if target4 is not None:
return json_data[target1][target2][target3][target4]
elif target3 is not None:
return json_data[target1][target2][target3]
elif target2 is not None:
return json_data[target1][target2]
elif target1 is not None:
return json_data[target1]
# json驱动
@pytest.mark.parametrize('topic_get_param,status_code,res',common.get_json_data('../global_val/topic_get_param.json',"topic_get_param"))
# 常量驱动
@pytest.mark.parametrize('topic_tab',tabparam)
# 对两个数据驱动中的数据进行组合并测试接口
def test_topic_get(topic_tab,topic_get_param,status_code,res):
r = requests.get(url=base_url+topic_id+tmp_topic_update_id[topic_tab],params=topic_get_param)
assert r.status_code == status_code
assert r.json()['success'] == res
print(tmp_topic_update_id[topic_tab])
print(json.dumps(r.json(),ensure_ascii=False,indent=4))
execl数据驱动
比较推荐的方法,只要定义好内容格式,在execl文件中就如平时写功能用例一般,输入对应的内容即可,对于编程能力一般或者新人来说,也能很好的理解。主要是方便给新人派活,不用一说接口自动化就只能特定的人干。
定义好execl内容格式,如必要的用例名称、接口地址、接口方法、接口数据、返回码、预期结果等。
根据需要自行编写适合的execl文件数据获取方法
def get_execl_data(filename,sheetname=None,s_row=None,s_column=None,f_row=None,f_column=None):
wb = load_workbook(filename)
if sheetname is None:
return wb.worksheets
else:
ws = wb[sheetname]
test_data = []
if s_row is None :
if f_row is None:
for rows in range(2, len(tuple(ws.rows))+1):
test_cell = []
if s_column is None:
if f_column is None:
for columns in range(1, len(tuple(ws.columns)) + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif f_column is not None:
for columns in range(1, f_column + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
print(ws.cell(row=rows, column=columns).value)
elif s_column is not None:
if f_column is None:
for columns in range(s_column, len(tuple(ws.columns)) + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif f_column is not None:
for columns in range(s_column, f_column + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
test_data.append(test_cell)
elif f_row is not None:
for rows in range(2, f_row+1):
test_cell = []
if s_column is None:
if f_column is None:
for columns in range(1, len(tuple(ws.columns)) + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif f_column is not None:
for columns in range(1, f_column + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif s_column is not None:
if f_column is None:
for columns in range(s_column, len(tuple(ws.columns)) + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif f_column is not None:
for columns in range(s_column, f_column + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
test_data.append(test_cell)
elif s_row is not None :
if f_row is None:
for rows in range(s_row, len(tuple(ws.rows)) + 1):
test_cell = []
if s_column is None:
if f_column is None:
for columns in range(1, len(tuple(ws.columns)) + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif f_column is not None:
for columns in range(1, f_column + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif s_column is not None:
if f_column is None:
for columns in range(s_column, len(tuple(ws.columns)) + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif f_column is not None:
for columns in range(s_column, f_column + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
test_data.append(test_cell)
elif f_row is not None:
for rows in range(s_row, f_row + 1):
test_cell = []
if s_column is None:
if f_column is None:
for columns in range(1, len(tuple(ws.columns)) + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif f_column is not None:
for columns in range(1, f_column + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif s_column is not None:
if f_column is None:
for columns in range(s_column, len(tuple(ws.columns)) + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
elif f_column is not None:
for columns in range(s_column, f_column + 1):
test_cell.append(ws.cell(row=rows, column=columns).value)
test_data.append(test_cell)
return test_data
将读取到的数据作为数据驱动,执行接口测试
execl_data = common.get_execl_data('../global_val/test_execl.xlsx',sheetname='Sheet1')
@pytest.mark.parametrize('case_name,base_url,api,method,data,code,res',execl_data)
def test_execl_data(case_name,base_url,api,method,data,code,res):
print(case_name)
if method == 'get':
r = requests.get(url=base_url+api,params=json.loads(data))
if method == 'post':
r = requests.post(url=base_url+api,data=json.loads(data))
print('当前用例的接口地址为: ',base_url+api,' 使用的接口方法为: ',method)
print('接口传参数据为: ',data)
assert r.status_code == code
assert r.json()['success'] == res
print(r.json())
后续再添加 allure 注释与执行过程日志输出即可。
个人目前的使用来说,execl 数据驱动方法简单快捷,适合公司新人上手自动化,并在后续的维护中也较为灵活。
对于需要接口关联的数据,也可以在测试过程中对数据写入到execl文件以供后续的接口使用,这块的操作更复杂些。