基于HttpRunner,解析swagger数据,快速生成接口测试框架
使用HttpRunner默认生成的项目是这样的
命令:httprunner --startproject 项目名称

so,根据这个项目的目录结构,使用python解析swagger接口参数,可以快速生成api、testcases、testsuites文件夹中用到的json文件
运行后的目录是这样的
api目录
按swagger中的tags区分为多个文件夹,每个文件夹下包含各自的api文件

testcases目录
按swagger中的tags区分为不同的json文件,每个文件包含所有的api接口

testsuites目录
测试用例集,组织运行所有的测试用例

这样,接口测试框架的简易架子就有了。接下来,需要补充api接口文件数据
略
运行结果

HttpRunner自带的report

附lib目录下的代码
swagger.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019/9/9 15:17
# @Author : lixiaofeng
# @Site :
# @File : swagger.py
# @Software: PyCharm import os, 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/api-docs?group=sign-api').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]
if not params['deprecated']: # 接口是否被弃用
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)
else:
logger.log_info(
'interface path: {}, if name: {}, is deprecated.'.format(key, params['description']))
break
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('').retrieve_data()
简单的实现了功能,代码有些粗糙~~~
processingJson.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019/9/9 15:18
# @Author : lixiaofeng
# @Site :
# @File : processingJson.py
# @Software: PyCharm 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__))
具体业务场景的测试,可以按照导入.har文件的方法快速生成,总之还是挺便捷的。
之前就有关注HttpRunner,终于有空花了一天时间,大致过了一遍中文文档,简单的记录下学习成果~~~
关键字:parameters 参数化
层级很重要 testcase
{
"config": {
"name": "添加发布会测试用例",
"variables": {}
},
"testcases": [
{
"name": "添加发布会",
"testcase": "testsuites/test.json",
"parameters": {
"limit": [
"",
""
]
}
}
]
}
teststeps
{
"config": {
"base_url": "http://www.easytest.xyz/api",
"name": "添加发布会",
"variables": {
"name": "${get_random_name()}",
"address": "${get_random_address()}",
"eid": "${get_random_num()}",
"limit": "${get_random_num_list()}"
},
"output": [
"limit"
]
},
"teststeps": [
{
"name": "添加发布会",
"request": {
"headers": {},
"data": {
"eid": "$eid",
"name": "$name",
"limit": "$limit",
"address": "$address",
"start_time": "2019-11-30",
"status": ""
},
"method": "POST",
"url": "/add_event/"
},
"validate": [
{
"eq": [
"content.message",
"add event success"
]
}
]
}
]
}
运行
from httprunner.api import HttpRunner
runner = HttpRunner(failfast=True)
runner.run(r'D:\HttpRunner_framework\testsuites\testcase_test.json')
print(runner.summary)
结果

基于HttpRunner,解析swagger数据,快速生成接口测试框架的更多相关文章
- 使用excel中的数据快速生成sql语句
在小公司的话,总是会有要开发去导入历史数据(数据从旧系统迁移到新系统上)的时候.这个时候,现场实施或客户会给你一份EXCEL文档,里面包含了一些别的系统上的历史数据,然后就让你导入到现在的系统上面去. ...
- 代码生成工具更新--快速生成Winform框架的界面项目
在之前版本的代码生成工具Database2Sharp中,由于代码生成都是考虑Winform和Web通用的目的,因此Winform界面或者Web界面都是单独生成的,在工具中生成相应的界面后,复制到项目里 ...
- 【Java POI】POI基于事件驱动解析大数据量2007版本Excel,空值导致列错位问题
1.目前测试了20M的文件,可以读取. 2.支持单个工作表1万+的数据行数,耗时如图. 3.以下是关键地方处理的代码 //Accepts objects needed while parsing. / ...
- 基于SpringBoot的Web API快速开发基础框架
其实还是很因为懒,才会有这个案例项目的产生,每次开启一个终端的小服务都要整理一次框架,造成重复的.不必要的.缺乏创造性的劳动,SO,本着可以用.用着简单的原则上传代码到Github,希望有需要的朋友直 ...
- 基于SpringBoot-Dubbo的微服务快速开发框架
简介: 基于Dubbo的分布式/微服务基础框架,为前端提供脚手架开发服务,结合前一篇--Web AP快速开发基础框架,可快速上手基于Dubbo的分布式服务开发,项目代码: https://github ...
- 基于Swagger+SpringBoot快速构建javaweb项目
章节导航 SpringBoot&Swagger简介 数据模型和接口定义 项目框架生成 业务逻辑实现 项目源码地址 github项目路径:https://github.com/Vikezhu/s ...
- 使用代码生成工具快速生成基于ABP框架的Vue+Element的前端界面
世界上唯一不变的东西就是变化,我们通过总结变化的规律,以规律来应付变化,一切事情处理起来事半功倍.我们在开发后端服务代码,前端界面代码的时候,界面都是依照一定的规律进行变化的,我们通过抽取数据库信息, ...
- iOS开发之JSON格式数据的生成与解析
本文将从四个方面对IOS开发中JSON格式数据的生成与解析进行讲解: 一.JSON是什么? 二.我们为什么要用JSON格式的数据? 三.如何生成JSON格式的数据? 四.如何解析JSON格式的数据? ...
- 转载 -- iOS开发之JSON格式数据的生成与解析
本文将从四个方面对IOS开发中JSON格式数据的生成与解析进行讲解: 一.JSON是什么? 二.我们为什么要用JSON格式的数据? 三.如何生成JSON格式的数据? 四.如何解析JSON格式的数据? ...
随机推荐
- Android_(游戏)打飞机01:前言
(游戏)打飞机01:前言 传送门 (游戏)打飞机02:游戏背景滚动 传送门 (游戏)打飞机03:控制玩家飞机 传送门 (游戏)打飞机04:绘画敌机.添加子弹 传送门 (游戏)打飞机05:处理子弹, ...
- Static class 与non static class的区别
内部静态类不需要有指向外部类的引用.但非静态内部类需要持有对外部类的引用.非静态内部类能够访问外部类的静态和非静态成员.静态类不能访问外部类的非静态成员.他只能访问外部类的静态成员.一个非静态内部类不 ...
- C++入门经典-例6.17-输出每行数组中的最小值
1:代码如下: // 6.17.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using ...
- 利用Zookeeper实现分布式锁
特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...
- 左值引用&右值引用实践【TODO】
这篇文章写的很好,下半部分还未完全理解,后续还需要回头来看看20190706(): https://www.cnblogs.com/likaiming/p/9045642.html 简单实践如下: # ...
- 12个Sublime Text应用技巧[转载]
本文为您提供Sublime Text编辑器的12个技巧和诀窍,深入挖掘这个看似简洁的代码编辑器,背后所隐藏的实现各种高级功能的无限可能. 1) 选择 以下是一些Sublime Text选择文本的快捷键 ...
- InputNumber计数器
InputNumber 计数器 仅允许输入标准的数字值,可定义范围 要使用它,只需要在el-input-number元素中使用v-model绑定变量即可,变量的初始值即为默认值. <templa ...
- C#三种常用的读取XML文件的方法
下面我将介绍三种常用的读取XML文件的方法.分别是 1: 使用 XmlDocument 2: 使用 XmlTextReader 3: 使用 Linq to Xml 这里我先创建一个XML文件,名为Bo ...
- HTML5 WebRTC API无需网络获取本地IP
因需求需要获取客户端的本机IP,国内资料基本上都是通过向一个IP网站发送请求并获取IP,这样有一定几率泄露自己的IP,在内网环境下也并不适用. 后来在stackoverflow上找到一种解决办法,用W ...
- Networking 基本术语/概念
目录 文章目录 目录 基本概念 冲突域(Collision Domain) 广播域(Broadcast Domain) 冲突域与广播域的区别 IP 网络数据传输方式 物理网络设备 发展简述 中继器(R ...