接口自动化框架HttpRunner实践
程序员文章站
2022-06-04 16:18:13
...
文章目录
1、安装Python(包括配置Python)
2、安装httprunner
pip install httprunner
3、生成脚手架
hrun --startproject petstore
4、html格式的swagger文档的访问地址
5、json格式的swagger文档的访问地址
https://petstore.swagger.io/v2/swagger.json
6、解析json格式的swagger文档,生成自动化用例
# -*- coding: utf-8 -*-
# @Time : 2019/9/9 15:17
# @Author :
# @Site :
# @File : swagger.py
# @Software: IDEA
import os
import requests
from httprunner import logger
from lib.processingJson import write_data, get_json
class AnalysisJson:
"""swagger自动生成测试用例"""
def __init__(self, url):
self.url = url
self.interface = {}
self.case_list = []
self.tags_list = []
self.http_suite = {"config": {"name": "", "base_url": "", "variables": {}},
"testcases": []}
self.http_testcase = {"name": "", "testcase": "", "variables": {}}
def retrieve_data(self):
"""
主函数
:return:
"""
try:
r = requests.get(self.url + '/v2/swagger.json').json()
write_data(r, 'data.json')
# r = get_json('D:\HttpRunner_framework\\testcases\data.json')
except Exception as e:
logger.log_error('请求swagger url 发生错误. 详情原因: {}'.format(e))
return 'error'
self.data = r['paths'] # 接口数据
self.url = 'https://' + r['host']
self.title = r['info']['title']
self.http_suite['config']['name'] = self.title
self.http_suite['config']['base_url'] = self.url
self.definitions = r['definitions'] # body参数
for tag_dict in r['tags']:
self.tags_list.append(tag_dict['name'])
i = 0
for tag in self.tags_list:
self.http_suite['testcases'].append({"name": "", "testcase": "", "variables": {}})
self.http_suite['testcases'][i]['name'] = tag
self.http_suite['testcases'][i]['testcase'] = 'testcases/' + tag + '.json'
i += 1
suite_path = os.path.join(os.path.abspath(os.path.join(os.path.dirname("__file__"), os.path.pardir)),
'testsuites')
testcase_path = os.path.join(suite_path, 'demo_testsuite.json')
write_data(self.http_suite, testcase_path)
if isinstance(self.data, dict):
for tag in self.tags_list:
self.http_case = {"config": {"name": "", "base_url": "", "variables": {}}, "teststeps": []}
for key, value in self.data.items():
for method in list(value.keys()):
params = value[method]
try:
if params['deprecated']: # 接口是否被弃用
logger.log_info(
'interface path: {}, if name: {}, is deprecated.'.format(key, params['description']))
break
except KeyError:
if params['tags'][0] == tag:
self.http_case['config']['name'] = params['tags'][0]
self.http_case['config']['base_url'] = self.url
case = self.retrieve_params(params, key, method, tag)
self.http_case['teststeps'].append(case)
api_path = os.path.join(os.path.abspath(os.path.join(os.path.dirname("__file__"), os.path.pardir)),
'testcases')
testcase_path = os.path.join(api_path, tag + '.json')
write_data(self.http_case, testcase_path)
else:
logger.log_error('解析接口数据异常!url 返回值 paths 中不是字典.')
return 'error'
def retrieve_params(self, params, api, method, tag):
"""
解析json,把每个接口数据都加入到一个字典中
:param params:
:param params_key:
:param method:
:param key:
:return:
replace('false', 'False').replace('true', 'True').replace('null','None')
"""
http_interface = {"name": "", "variables": {},
"request": {"url": "", "method": "", "headers": {}, "json": {}, "params": {}}, "validate": [],
"output": []}
http_testcase = {"name": "", "api": "", "variables": {}, "validate": [], "extract": [], "output": []}
name = params['summary'].replace('/', '_')
http_interface['name'] = name
http_testcase['name'] = name
http_testcase['api'] = 'api/{}/{}.json'.format(tag, name)
http_interface['request']['method'] = method.upper()
http_interface['request']['url'] = api.replace('{', '$').replace('}', '')
parameters = params.get('parameters') # 未解析的参数字典
responses = params.get('responses')
if not parameters: # 确保参数字典存在
parameters = {}
for each in parameters:
if each.get('in') == 'body': # body 和 query 不会同时出现
schema = each.get('schema')
if schema:
ref = schema.get('$ref')
if ref:
param_key = ref.split('/')[-1]
param = self.definitions[param_key]['properties']
for key, value in param.items():
if 'example' in value.keys():
http_interface['request']['json'].update({key: value['example']})
else:
http_interface['request']['json'].update({key: ''})
elif each.get('in') == 'query':
name = each.get('name')
for key in each.keys():
if 'example' in key:
http_interface['request']['params'].update({name: each[key]})
for each in parameters:
# if each.get('in') == 'path':
# name = each.get('name')
# for key in each.keys():
# if 'example' in key:
# http_interface['request']['json'].update({name: each[key]})
# else:
#
# http_interface['request']['json'].update({name: ''})
if each.get('in') == 'header':
name = each.get('name')
for key in each.keys():
if 'example' in key:
http_interface['request']['headers'].update({name: each[key]})
else:
if name == 'token':
http_interface['request']['headers'].update({name: '$token'})
else:
http_interface['request']['headers'].update({name: ''})
for key, value in responses.items():
schema = value.get('schema')
if schema:
ref = schema.get('$ref')
if ref:
param_key = ref.split('/')[-1]
res = self.definitions[param_key]['properties']
i = 0
for k, v in res.items():
if 'example' in v.keys():
http_interface['validate'].append({"eq": []})
http_interface['validate'][i]['eq'].append('content.' + k)
http_interface['validate'][i]['eq'].append(v['example'])
http_testcase['validate'].append({"eq": []})
http_testcase['validate'][i]['eq'].append('content.' + k)
http_testcase['validate'][i]['eq'].append(v['example'])
i += 1
else:
http_interface['validate'].append({"eq": []})
else:
http_interface['validate'].append({"eq": []})
if http_interface['request']['json'] == {}:
del http_interface['request']['json']
if http_interface['request']['params'] == {}:
del http_interface['request']['params']
api_path = os.path.join(os.path.abspath(os.path.join(os.path.dirname("__file__"), os.path.pardir)), 'api')
tags_path = os.path.join(api_path, tag)
if not os.path.exists(tags_path):
os.mkdir(tags_path)
json_path = os.path.join(tags_path, http_interface['name'] + '.json')
write_data(http_interface, json_path)
return http_testcase
if __name__ == '__main__':
AnalysisJson('https://petstore.swagger.io').retrieve_data()
# -*- coding: utf-8 -*-
# @Time : 2019/9/9 15:18
# @Author :
# @Site :
# @File : processingJson.py
# @Software: IDEA
import json
from httprunner import logger
def get_json(path, field=''):
"""
获取json文件中的值,data.json和res.json可共用
:param path:
:param field:
:return:
"""
with open(path, 'r', encoding='utf-8') as f:
json_data = json.load(f)
if field:
data = json_data.get(field)
return data
else:
return json_data
def write_data(res, json_path):
"""
把处理后的参数写入json文件
:param res:
:param json_path:
:return:
"""
if isinstance(res, dict) or isinstance(res, list):
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(res, f, ensure_ascii=False, sort_keys=True, indent=4)
logger.log_info('Interface Params Total:{} ,write to json file successfully!\n'.format(len(res)))
else:
logger.log_error('{} Params is not dict.\n'.format(write_data.__name__))
7、宠物相关的接口(自动生成的)
{
"name": "Add a new pet to the store",
"output": [],
"request": {
"headers": {"Accept": "application/json"},
"json": {
"category": {},
"id": "",
"name": "$name",
"photoUrls": [],
"status": "$status",
"tags": []
},
"method": "POST",
"url": "/pet"
},
"validate": [],
"variables": {}
}
{
"name": "Deletes a pet",
"output": [],
"request": {
"headers": {
"Accept": "application/json",
"api_key": ""
},
"method": "DELETE",
"url": "/pet/$id"
},
"validate": [],
"variables": {}
}
{
"name": "Find pet by ID",
"output": [],
"request": {
"headers": {"Accept": "application/json"},
"method": "GET",
"url": "/pet/$id"
},
"validate": [],
"variables": {}
}
{
"name": "Finds Pets by status",
"output": [],
"request": {
"headers": {"Accept": "application/json"},
"method": "GET",
"url": "/pet/findByStatus?status=$status"
},
"validate": [],
"variables": {}
}
{
"name": "Update an existing pet",
"output": [],
"request": {
"headers": {"Accept": "application/json"},
"json": {
"category": {},
"id": "$petId",
"name": "$name",
"photoUrls": [],
"status": "available",
"tags": []
},
"method": "PUT",
"url": "/pet"
},
"validate": [],
"variables": {}
}
{
"name": "Updates a pet in the store with form data",
"output": [],
"request": {
"headers": {"Accept": "application/json"},
"method": "POST",
"url": "/pet/$petId",
"data": {
"name": "$name",
"status": "$status"
}
},
"validate": [],
"variables": {}
}
{
"name": "uploads an image",
"output": [],
"request": {
"headers": {"Accept": "application/json"},
"method": "POST",
"url": "/pet/$petId/uploadImage",
"files": {"file": ["pet.jpg","${get_file($filePath)}","image/jpeg"]}
},
"validate": [],
"variables": {}
}
8、自定义函数get_file
# -*- coding: utf-8 -*-
# @Time : 2019/12/08 17:34
# @Author : yangzc
# @Site :
# @File : debugtalk.py
# @Software: IDEA
import time
def sleep(n_secs):
time.sleep(n_secs)
# 读取文件内容
def get_file(file_path):
return open(file_path, "rb")
9、宠物接口的测试用例(自动生成的)
{
"config": {
"base_url": "https://petstore.swagger.io/v2",
"name": "pet",
"variables": {}
},
"teststeps": [
{
"api": "api/pet/Add a new pet to the store.json",
"extract": [{"id": "content.id"}],
"name": "Add a new pet to the store",
"output": [],
"validate": [],
"variables": {
"name": "小猪佩奇",
"status": "available"
}
},
{
"api": "api/pet/Find pet by ID.json",
"extract": [],
"name": "Find pet by ID",
"output": [],
"validate": [
{
"eq": [
"content.status",
"available"
]
}
],
"variables": {}
},
{
"api": "api/pet/Finds Pets by status.json",
"extract": [],
"name": "Finds Pets by status",
"output": [],
"validate": [
{
"eq": [
"status_code",
200
]
}
],
"variables": {"status": "sold"}
},
{
"api": "api/pet/uploads an image.json",
"extract": [],
"name": "uploads an image",
"output": [],
"validate": [
{
"eq": [
"status_code",
200
]
}
],
"variables": {
"petId": "9216678377732767000",
"filePath": "C:\\Users\\yangzc\\Desktop\\pet.jpg"}
},
{
"api": "api/pet/Update an existing pet.json",
"extract": [],
"name": "Update an existing pet",
"output": [],
"validate": [
{
"eq": [
"status_code",
200
]
},
{
"eq": [
"content.name",
"喜羊羊"
]
}
],
"variables": {
"petId": "9216678377732767000",
"name": "喜羊羊"}
},
{
"api": "api/pet/Updates a pet in the store with form data.json",
"extract": [],
"name": "Updates a pet in the store with form data",
"output": [],
"validate": [
{
"eq": [
"status_code",
200
]
}
],
"variables": {
"petId": "9216678377732767000",
"name": "灰太狼",
"status": "sold"
}
},
{
"api": "api/pet/Deletes a pet.json",
"extract": [],
"name": "Deletes a pet",
"output": [],
"validate": [
{
"eq": [
"status_code",
200
]
}
],
"variables": {}
}
]
}
10、自动生成的测试集
{
"config": {
"base_url": "https://petstore.swagger.io",
"name": "Swagger Petstore",
"variables": {}
},
"testcases": [
{
"name": "pet",
"testcase": "testcases/pet.json",
"variables": {}
},
{
"name": "store",
"testcase": "testcases/store.json",
"variables": {}
},
{
"name": "user",
"testcase": "testcases/user.json",
"variables": {}
}
]
}
10、运行测试集
hrun .\testsuites\demo_testsuite.json
11、测试报告
12、参考资料
[01] HttpRunner中文用户手册
[02] 基于HttpRunner,解析swagger数据,快速生成接口测试框架
[03] 开源啦~接口自动化测试平台
[04] 基于 HttpRunner 的 Web 测试平台:HttpRunnerManager
[05] httprunner学习25-文件上传multipart/form-data
[06] swagger api一键导入postman
微信扫一扫关注公众号
点击链接加入群聊
https://jq.qq.com/?_wv=1027&k=5eVEhfN
软件测试学习交流QQ群号:511619105
上一篇: 婴幼儿酱油是否必要 专家提醒以母乳为主
下一篇: 婴儿腹泻 五类止泻药须慎用
推荐阅读
-
python自动化框架搭建过程(分享python接口自动化框架有哪些)
-
python自动化框架搭建过程(分享python接口自动化框架有哪些)
-
Python http接口自动化测试框架实现方法示例
-
接口自动化pytest+allure框架基本理解-pytest(五)
-
接口测试框架接入性能测试实践分享
-
基于Python的接口自动化unittest测试框架和ddt数据驱动详解
-
一个Python最简单的接口自动化框架
-
Pytest接口自动化测试框架搭建模板
-
Python+unittest+requests+excel实现接口自动化测试框架
-
Python接口自动化测试框架(python3+requests+excel)