接口自动化框架HttpRunner实践

1、安装Python(包括配置Python)

2、安装httprunner

pip install httprunner

在这里插入图片描述

3、生成脚手架

hrun --startproject petstore

在这里插入图片描述

4、html格式的swagger文档的访问地址

http://petstore.swagger.io/

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
[07] 接口测试-录制har文件
[08] 通过 Har 生成测试脚本 (LR 为例)
[09] 接口测试是否有必要自动生成 HAR 文件
[10] HttpRunner通过抓包软件生成.har,再将.har文件转换为.yml文件的方法与步骤

微信扫一扫关注公众号
image.png
点击链接加入群聊

https://jq.qq.com/?_wv=1027&k=5eVEhfN
软件测试学习交流QQ群号:511619105

软件测试学习资料

《自动化测试教程》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值