swagger 自动生成接口测试用例


---整体更新一波---
  1.实际工作中,因为要动手输入的地方比较多,自动生成的异常接口用例感觉用处不大,就先去掉了,只保留了正常的;
  2.接口有改动的,如果开发人员没有及时告知或没有详细告知,会增加一些不必要的麻烦,所以增加了文件对比功能;

目录:
  

 

case_generate.py
import sys

sys.path.append('D:\Interface_framework_Beauty')

import requests
import os
from common.operation_excel import Write_excel  # 写入excel模块
from common.logger import Log  # 打印日志模块
from common.processing_json import write_data  # 写入json文件模块
from common.difference import diff_excel, diff_json
from common import read_config

title_list = []

old_excel_path = os.path.abspath(
    os.path.dirname(os.path.dirname(__file__))) + '\\case_generate' + '\\data_old' + '\\demo_api.xlsx'
excel_path = os.path.abspath(
    os.path.dirname(
        os.path.dirname(__file__))) + '\\case_generate' + '\\data_new' + '\\demo_api.xlsx'  # case path
old_json_path = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + '\\case_generate' + '\\data_old'
json_path = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + '\\case_generate' + '\\data_new'


class AnalysisJson:
    """swagger自动生成测试用例"""

    def __init__(self, url_json):
        self.url_json = url_json
        r = requests.get(self.url_json + '/v2/api-docs?group=sign-api').json()
        self.title = r['info']['description']
        write_data(r, '{}.json'.format(self.title))
        self.interface_params = {}
        self.log = Log()
        self.row = 2  # 写入excel起始行数
        self.num = 1  # case id
        global title_list, json_path
        if self.check_data(r):
            self.json_path = os.path.abspath(
                os.path.dirname(
                    os.path.dirname(__file__))) + '\\case_generate' + '\\data_new' + '\\{}_data.json'.format(
                self.title)  # json file path,执行多个url的情况,区分生成的json文件
        self.data = r['paths']  # paths中的数据是有用的

        title_list.append(self.title)

    def check_data(self, r):
        """检查返回的数据是否是dict"""
        if not isinstance(r, dict):
            self.log.info('swagger return json error.')
            return False
        else:
            return True

    def retrieve_data(self):
        """主函数"""
        global body_name, method
        for k, v in self.data.items():
            method_list = []
            for _k, _v in v.items():
                interface = {}
                if not _v['deprecated']:  # 接口是否被弃用
                    method_list.append(_k)
                    api = k  # api地址
                    if len(method_list) > 1:  # api地址下的请求方式不止一个的情况
                        for i in range(len(method_list)):
                            body_name = api.replace('/', '_') + '_' * i  # json文件对应参数名称,excel中body名称
                            method = method_list[-1]  # 请求方式 同一个api地址,不同请求方式
                    else:
                        body_name = api.replace('/', '_')
                        method = _k
                    self.interface_params = self.retrieve_excel(_v, interface, api)
                else:
                    self.log.info('interface path: {}, case name: {}, is deprecated.'.format(k, _v['description']))
                    break
        if self.interface_params:
            write_data(self.interface_params, self.json_path)  # 参数写入json文件

    def retrieve_excel(self, _v, interface, api):
        """解析参数,拼接为dict--准备完成写入excel的数据"""
        parameters = _v.get('parameters')  # 未解析的参数字典
        if not parameters:  # 确保参数字典存在
            parameters = {}
        case_name = _v['summary']  # 接口名称
        tags = _v['tags'][0]  # 标签名称
        params_dict = self.retrieve_params(parameters)  # 处理接口参数,拼成dict形式
        if params_dict and parameters != {}:  # 单个或多个参数
            interface['row_num'] = self.row  # 写入excel时的所在行
            interface['id'] = 'test_' + str(self.num)  # case id
            interface['tags'] = tags  # 标签名称
            interface['name'] = case_name
            _type = 'json'  # 参数获取方式
            interface['method'] = method  # 请求方式
            interface['url'] = self.url_json + api  # 拼接完成接口url
            interface['headers'] = 'yes'  # 是否传header
            interface['body'] = body_name
            interface['type'] = _type
            self.num += 1
            self.row += 1
            self.interface_params[body_name] = params_dict
            self.write_excel(interface, excel_path)  # 参数写入excel
        else:  # 没有参数
            _type = 'data_old'
            interface['name'] = case_name
            interface['row_num'] = self.row
            interface['id'] = 'test_' + str(self.num)
            interface['tags'] = tags
            interface['method'] = method
            interface['url'] = self.url_json + api
            interface['headers'] = 'yes'
            interface['body'] = ''
            interface['type'] = _type
            self.num += 1
            self.row += 1
            self.interface_params[body_name] = params_dict
            self.write_excel(interface, excel_path)
        return self.interface_params

    def retrieve_params(self, parameters):
        """处理参数,转为dict"""
        params = ''
        _in = ''
        for each in parameters:
            _in += each.get('in') + '\n'  # 参数传递位置
            params += each.get('name') + '\n'  # 参数
        _in = _in.strip('\n')
        _in_list = _in.split('\n')
        params = params.strip('\n')
        params_list = params.split('\n')
        del_list = params_list.copy()
        for i in range(len(_in_list)):
            if _in_list[i] == 'header':
                params_list.remove(del_list[i])  # 只保存在body传的参数
        test_list = params_list.copy()
        params_dict = dict(zip(params_list, test_list))  # 把list转为dict
        return params_dict

    def write_excel(self, interface, filename):
        """把dict中的值写入对应的excel行中"""
        wt = Write_excel(filename, self.title)
        try:
            wt.write(interface['row_num'], 1, interface['id'])
            wt.write(interface['row_num'], 2, interface['tags'])
            wt.write(interface['row_num'], 3, interface['name'])
            wt.write(interface['row_num'], 4, interface['method'])
            wt.write(interface['row_num'], 5, interface['url'])
            wt.write(interface['row_num'], 7, interface['headers'])
            wt.write(interface['row_num'], 8, interface['body'])
            wt.write(interface['row_num'], 10, interface['type'])
            self.log.info('Interface case id {},write to excel file successfully!'.format(interface['id']))
        except Exception as e:
            self.log.info('Failure of interface use case to write to excel file! error:{}\n'.format(e))
            return


def diff_file():
    """对比文件"""
    global title_list
    for title in title_list:
        diff_excel(old_excel_path, excel_path, title)
        diff_json(os.path.join(old_json_path, '{}_data.json'.format(title)),
                  os.path.join(json_path, '{}_data.json'.format(title)), title)


if __name__ == '__main__':
    url = read_config.generate_url.split(',')
    for i in url:
        # url_json = i + '/v2/api-docs?group=sign-api'  # json swagger url地址
        AnalysisJson(i).retrieve_data()
    diff_file()

difference.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2019/3/17 21:35
# @Author  : lixiaofeng
# @File    : difference.py
# @Software: PyCharm
# @desc    : 对比接口excel, json更新

import xlrd, time, difflib, sys, os
from common.logger import Log, report_path

log = Log()


def diff_excel(ori_path, tar_path, sub_name):
    """比较excel文件"""
    success = 0  # 匹配一致数量
    fail = 0  # 匹配不一致数量
    origin_xls = {}  # 存储源xls文件
    target_xls = {}  # 比对的xls文件
    wb_ori = xlrd.open_workbook(ori_path)  # 打开原始文件
    wb_tar = xlrd.open_workbook(tar_path)  # 打开目标文件
    log.info(':【开始比对】...' + '\n')  # 写入开始时间

    try:
        sheet_ori = wb_ori.sheet_by_name(sub_name)
        sheet_tar = wb_tar.sheet_by_name(sub_name)
        if sheet_ori.name == sheet_tar.name:
            # sheet表名
            if sheet_ori.name == sub_name:
                # 先将数存入dictionary中dictionary(rows:list)
                # 第一行存储表头
                # 源表取一行数据与目标表全表进行比对如果表中存在主键可以用主键进行索引
                # 数据从excel第3行开始
                for rows in range(1, sheet_ori.nrows):
                    orign_list = sheet_ori.row_values(rows)  # 源表i行数据
                    target_list = sheet_tar.row_values(rows)  # 目标表i行数据
                    origin_xls[rows] = orign_list  # 源表写入字典
                    target_xls[rows] = target_list  # 目标表写入字典
                if origin_xls[1] == target_xls[1]:
                    log.info('>>>>>>>>>>>>>>>>>>> 表头一致')
                for ori_num in origin_xls:
                    flag = 'false'  # 判断是否一致标志
                    for tar_num in target_xls:
                        if origin_xls[ori_num] == target_xls[tar_num]:
                            flag = 'true'
                            break  # 如果匹配到结果退出循环
                    if flag == 'true':  # 匹配上结果输出后台日志
                        success += 1
                    else:  # 匹配不上将源表中行记录写入log
                        fail += 1
                        data = origin_xls[ori_num]
                        logstr = '【不一致】row<' + str(ori_num) + '>:' + str(data)
                        log.info(logstr)
                logstr = '【比对完成】总记录数:{:d}条,一致:{:d}条,不一致:{:d}条'.format(ori_num, success, fail)
                log.info(logstr)
        else:
            errmsg = '' + sub_name + '】子表名不一致'
            log.info(errmsg)
    except Exception as err:
        log.info(str(err))  # 输出异常


# 创建打开文件函数,并按换行符分割内容
def read_json(filename):
    try:
        with open(filename, 'r') as fileHandle:
            text = fileHandle.read().splitlines()
        return text
    except IOError as e:
        log.error("Read file Error:" + e)
        sys.exit()


# 比较两个文件并输出到html文件中
def diff_json(filename1, filename2, name):
    text1_lines = read_json(filename1)
    text2_lines = read_json(filename2)
    d = difflib.HtmlDiff()
    # context=True时只显示差异的上下文,默认显示5行,由numlines参数控制,context=False显示全文,差异部分颜色高亮,默认为显示全文
    result = d.make_file(text1_lines, text2_lines, filename1, filename2, context=True)
    # 内容保存到result.html文件中
    log.info('json数据比对结果写入html中.')
    with open(os.path.join(report_path, '{}_diff.html'.format(name)), 'w') as result_file:
        result_file.write(result)


if __name__ == '__main__':
    for i in ['前台api', '后台api']:
        diff_excel(r'G:\Interface_framework_Beauty\case_generate\data_old\demo_api.xlsx',
                   'G:\Interface_framework_Beauty\case_generate\data_new\demo_api.xlsx', '{}'.format(i))
    diff_json('G:\Interface_framework_Beauty\case_generate\data_old\前台api_data.json',
              'G:\Interface_framework_Beauty\case_generate\data_new\前台api_data.json', '')

执行成功后自动生成的 case,因为我使用的是ddt+requests+unittest框架,所以要把case集成到excel中。断言什么的需要手动输入...

 

data.json文件。使用的参数值需要手动输入...

 

生成的日志文件。从日志文件可以看出,case条数和对应的json参数数量是一致的.

 

 可以根据生成的对比结果来修改接口数据

excel文件生成的log对比结果:

 json文件生成的html对比结果:

 

各位大大有其他好的方法,欢迎留言一起讨论。

 

 

--------------------假装这里是分割线---------------------

writeExcel.py 文件
# coding:utf-8
from openpyxl import load_workbook
import openpyxl
from openpyxl.styles import Font, colors


class Write_excel(object):
    """修改excel数据"""

    def __init__(self, filename):
        self.filename = filename
        self.wb = load_workbook(self.filename)
        self.ws = self.wb.active  # 激活sheet

    def write(self, row_n, col_n, value):
        """写入数据,如(2,3,"hello"),第二行第三列写入数据"hello\""""
        ft = Font(color=colors.RED, size=12, bold=True)
        # 判断值为错误时添加字体样式
        if value in ['fail', 'error'] or col_n == 12:
            self.ws.cell(row_n, col_n).font = ft
        if value == 'pass':
            ft = Font(color=colors.GREEN)
            self.ws.cell(row_n, col_n).font = ft
        self.ws.cell(row_n, col_n).value = value
        self.wb.save(self.filename)


if __name__ == "__main__":
    # copy_excel("demo_api.xlsx", "test111.xlsx")
    wt = Write_excel("test111.xlsx")
    wt.write(4, 5, "HELLEOP")
    wt.write(4, 6, "HELLEOP")
processingJson.py 文件
import json
from common.logger import Log
from jsonpath_rw import jsonpath, parse


def write_data(res, json_path):
    """把处理后的参数写入json文件"""
    if isinstance(res, dict):
        with open(json_path, 'w', encoding='utf-8') as f:
            json.dump(res, f, indent=4)
            Log().info('Interface Params Total:{} ,write to json file successfully!\n'.format(len(res)))
    else:
        Log().info('{} Params is not dict.\n'.format(write_data.__name__))

 

logger.py 参考另一篇即可:python logging模块 输出日志和过期清理


此功能已集成到 EasyTest测试平台 中,欢迎大家体验~~~
posted @ 2018-09-23 02:38  backlightズ  阅读(21932)  评论(15编辑  收藏  举报