接口自动化框架---升级版(Pytest+request+Allure)

news2024/11/17 15:54:31

目录:导读

一、简单介绍

二、目录介绍

三、代码分析

写在最后


 

接口自动化是指模拟程序接口层面的自动化,由于接口不易变更,维护成本更小,所以深受各大公司的喜爱。
第一版入口:接口自动化框架(Pytest+request+Allure)
本次版本做了一些升级,增加了自动生成testcase等,一起来看看吧!~~


一、简单介绍

环境:Mac+Python 3+Pytest+Allure+Request
流程:Charles导出接口数据-自动生成测试用例-修改测试用例-执行测试用例-生成Allure报告
开源: 点击这里,跳转到github
备注⚠️:Charles导出接口应选择文件类型为JSON Session File(.chlsj)

重要模块介绍:
1、writeCase.py :自动读取新的Charles文件,并自动生成测试用例
2、apiMethod.py:封装request方法,可以支持多协议扩展(get\post\put)
3、checkResult.py:封装验证response方法
4、setupMain.py: 核心代码,定义并执行用例集,生成报告


二、目录介绍


三、代码分析

1、测试数据yml(自动生成的yml文件)

# 用例基本信息
test_info:
      # 用例标题,在报告中作为一级目录显示
      title: blogpost
      # 用例ID
      id: test_reco_01
      # 请求的域名,可写死,也可写成模板关联host配置文件
      host: ${host}$
      # 请求地址 选填(此处不填,每条用例必填)
      address: /api/v2/recomm/blogpost/reco

# 前置条件,case之前需关联的接口
premise:

# 测试用例
test_case:
    - test_name: reco_1
      # 第一条case,info可不填
      info: reco
      # 请求协议
      http_type: https
      # 请求类型
      request_type: POST
      # 参数类型
      parameter_type: application/json
      # 请求地址
      address: /api/v2/recomm/blogpost/reco
      # 请求头
      headers:
      # parameter为文件路径时
      parameter: reco.json
      # 是否需要获取cookie
      cookies: False
      # 是否为上传文件的接口
      file: false
      # 超时时间
      timeout: 20

      # 校验列表  list or dict
      # 不校验时 expected_code, expected_request 均可不填
      check:
        expected_request: result_reco.json
        check_type: only_check_status
        expected_code: 503

      # 关联键
      relevance:

2、测试case(自动生成的case.py)

@allure.feature(case_dict["test_info"]["title"])
class TestReco:

    @pytest.mark.parametrize("case_data", case_dict["test_case"], ids=[])
    @allure.story("reco")
    @pytest.mark.flaky(reruns=3, reruns_delay=3)
    def test_reco(self, case_data):
        """

        :param case_data: 测试用例
        :return:
        """
        self.init_relevance = ini_request(case_dict, PATH)
        # 发送测试请求
        api_send_check(case_data, case_dict, self.init_relevance, PATH)

3、writeCase.py (封装方法:自动生成测试case)

def write_case(_path):
    yml_list = write_case_yml(_path)
    project_path = str(os.path.abspath('.').split('/bin')[0])
    test_path = project_path+'/aff/testcase/'
    src = test_path+'Template.py'

    for case in yml_list:
        yml_path = case.split('/')[0]
        yml_name = case.split('/')[1]
        case_name = 'test_' + yml_name + '.py'
        new_case = test_path + yml_path + '/' + case_name
        mk_dir(test_path + yml_path)
        if case_name in os.listdir(test_path + yml_path):
            pass
        else:
            shutil.copyfile(src, new_case)
            with open(new_case, 'r') as fw:
                source = fw.readlines()
            n = 0
            with open(new_case, 'w') as f:
                for line in source:
                    if 'PATH = setupMain.PATH' in line:
                        line = line.replace("/aff/page/offer", "/aff/page/%s" % yml_path)
                        f.write(line)
                        n = n+1
                    elif 'case_dict = ini_case' in line:
                        line = line.replace("Template", yml_name)
                        f.write(line)
                        n = n + 1
                    elif 'class TestTemplate' in line:
                        line = line.replace("TestTemplate", "Test%s" % yml_name.title().replace("_", ""))
                        f.write(line)
                        n = n + 1
                    elif '@allure.story' in line:
                        line = line.replace("Template", yml_name)
                        f.write(line)
                        n = n + 1
                    elif 'def test_template' in line:
                        line = line.replace("template", yml_name.lower())
                        f.write(line)
                        n = n + 1

                    else:
                        f.write(line)
                        n += 1
                for i in range(n, len(source)):
                    f.write(source[i])

4、apiMethod.py(封装方法:http多协议)

def post(header, address, request_parameter_type, timeout=8, data=None, files=None):
    """
    post请求
    :param header: 请求头
    :param address: 请求地址
    :param request_parameter_type: 请求参数格式(form_data,raw)
    :param timeout: 超时时间
    :param data: 请求参数
    :param files: 文件路径
    :return:
    """
    if 'form_data' in request_parameter_type:
        for i in files:
            value = files[i]
            if '/' in value:
                file_parm = i
                files[file_parm] = (os.path.basename(value), open(value, 'rb'))
        enc = MultipartEncoder(
            fields=files,
            boundary='--------------' + str(random.randint(1e28, 1e29 - 1))
        )
        header['Content-Type'] = enc.content_type

        response = requests.post(url=address, data=enc, headers=header, timeout=timeout)
    else:
        response = requests.post(url=address, data=data, headers=header, timeout=timeout, files=files)
    try:
        if response.status_code != 200:
            return response.status_code, response.text
        else:
            return response.status_code, response.json()
    except json.decoder.JSONDecodeError:
        return response.status_code, ''
    except simplejson.errors.JSONDecodeError:
        return response.status_code, ''
    except Exception as e:
        logging.exception('ERROR')
        logging.error(e)
        raise

5、checkResult.py(封装方法:校验response结果)

def check_result(test_name, case, code, data, _path, relevance=None):
    """
    校验测试结果
    :param test_name: 测试名称
    :param case: 测试用例
    :param code: HTTP状态
    :param data: 返回的接口json数据
    :param relevance: 关联值对象
    :param _path: case路径
    :return:
    """
    # 不校验结果
    if case["check_type"] == 'no_check':
        with allure.step("不校验结果"):
            pass
    # json格式校验
    elif case["check_type"] == 'json':
        expected_request = case["expected_request"]
        if isinstance(case["expected_request"], str):
            expected_request = readExpectedResult.read_json(test_name, expected_request, _path, relevance)
        with allure.step("JSON格式校验"):
            allure.attach("期望code", str(case["expected_code"]))
            allure.attach('期望data', str(expected_request))
            allure.attach("实际code", str(code))
            allure.attach('实际data', str(data))
        if int(code) == case["expected_code"]:
            if not data:
                data = "{}"
            check_json(expected_request, data)
        else:
            raise Exception("http状态码错误!\n %s != %s" % (code, case["expected_code"]))
    # 只校验状态码
    elif case["check_type"] == 'only_check_status':
        with allure.step("校验HTTP状态"):
            allure.attach("期望code", str(case["expected_code"]))
            allure.attach("实际code", str(code))
            allure.attach('实际data', str(data))
        if int(code) == case["expected_code"]:
            pass
        else:
            raise Exception("http状态码错误!\n %s != %s" % (code, case["expected_code"]))
    # 完全校验
    elif case["check_type"] == 'entirely_check':
        expected_request = case["expected_request"]
        if isinstance(case["expected_request"], str):
            expected_request = readExpectedResult.read_json(test_name, expected_request, _path, relevance)
        with allure.step("完全校验"):
            allure.attach("期望code", str(case["expected_code"]))
            allure.attach('期望data', str(expected_request))
            allure.attach("实际code", str(code))
            allure.attach('实际data', str(data))
        if int(code) == case["expected_code"]:
            result = operator.eq(expected_request, data)
            if result:
                pass
            else:
                raise Exception("完全校验失败! %s ! = %s" % (expected_request, data))
        else:
            raise Exception("http状态码错误!\n %s != %s" % (code, case["expected_code"]))
    # 正则校验
    elif case["check_type"] == 'Regular_check':
        if int(code) == case["expected_code"]:
            try:
                result = ""
                if isinstance(case["expected_request"], list):
                    for i in case[""]:
                        result = re.findall(i.replace("\"","\""), str(data))
                        allure.attach('校验完成结果\n',str(result))
                else:
                    result = re.findall(case["expected_request"].replace("\"", "\'"), str(data))
                    with allure.step("正则校验"):
                        allure.attach("期望code", str(case["expected_code"]))
                        allure.attach('正则表达式', str(case["expected_request"]).replace("\'", "\""))
                        allure.attach("实际code", str(code))
                        allure.attach('实际data', str(data))
                        allure.attach(case["expected_request"].replace("\"", "\'") + '校验完成结果',
                                      str(result).replace("\'", "\""))
                if not result:
                    raise Exception("正则未校验到内容! %s" % case["expected_request"])
            except KeyError:
                raise Exception("正则校验执行失败! %s\n正则表达式为空时" % case["expected_request"])
        else:
            raise Exception("http状态码错误!\n %s != %s" % (code, case["expected_code"]))

    else:
        raise Exception("无该校验方式%s" % case["check_type"])

6、setupMain.py(执行用例集,生成测试报告)

def invoke(md):
    output, errors = subprocess.Popen(md, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
    o = output.decode("utf-8")
    return o


if __name__ == '__main__':
    LogConfig(PATH)
    write_case(har_path)
    args = ['-s', '-q', '--alluredir', xml_report_path]
    pytest.main(args)
    cmd = 'allure generate %s -o %s' % (xml_report_path, html_report_path)
    invoke(cmd)

7、测试报告

写在最后

如果你觉得文章还不错,请大家 点赞、分享、留言 下,因为这将是我持续输出更多优质文章的最强动力!

看到这篇文章的人有觉得我的理解有误的地方,也欢迎评论和探讨~

你也可以加入下方的的群聊去和同行大神交流切磋

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/375263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Android Studio] Android Studio使用keytool工具读取Debug 调试版数字证书以及release 发布版数字证书

🟧🟨🟩🟦🟪 Android Debug🟧🟨🟩🟦🟪 Topic 发布安卓学习过程中遇到问题解决过程,希望我的解决方案可以对小伙伴们有帮助。 📋笔记目…

学生宿舍管理系统

技术:Java、JSP等摘要:管理信息系统在现代社会已深入到各行各业,由于计算机技术的迅速发展和普及,信息管理系统MIS事实上已成为计算机管理信息系统,大学生宿舍管理系统就是一个典型的管理信息系统,它可以让宿舍管理工作…

【算法题】最大矩形面积,单调栈解法

力扣:84. 柱状图中最大的矩形 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 题意很简单,翻译一下就是:求该图中…

模拟银行存取钱-课后程序(JAVA基础案例教程-黑马程序员编著-第八章-课后作业)

【案例8-3】 模拟银行存取钱 【案例介绍】 1.任务描述 在银行办理业务时,通常银行会开多个窗口,客户排队等候,窗口办理完业务,会呼叫下一个用户办理业务。本案例要求编写一个程序模拟银行存取钱业务办理。假如有两个用户在存取…

【Linux】-- POSIX信号量

目录 POSIX信号量 sem_init - 初始化信号量 sem_destroy - 销毁信号量 sem_wait - 等待信号量(P操作) 基于环形队列的生产消费模型 数据结构 - 环形结构 实现原理 POSIX信号量 #问:什么是信号量? 1. 共享资源 -> 任何一…

2. 驱动开发--驱动开发环境搭建

文章目录前言一、Linux中配置编译环境1.1 linux下安装软件的方法1.2 交叉编译工具链的安装1.2.1 测试是否安装成功1.3 设置环境变量1.3.1 将工具链导出到环境变量1.4 为工具链创建arm-linux-xxx符号链接二、 搭建运行开发环境2.1 tftp网络方式加载内核和设备树文件2.2 nfs网络方…

大事很妙,跨境电商用Reddit做营销做测评真的很有用

最近呢,东哥在和一个叫 jens 的海外社媒大佬聊天,聊起了Reddit,其实 Reddit 可是个不错的流量平台,里面有不少宝藏,跟我们国内的贴吧差不多啦。 作为美国热度排名前五的社交网站,流量如此不错的平台&#…

3、Improved Denoising Diffusion Probabilistic Models#

简介论文发现通过一些简单的修改,ddpm也可以在保持高样本质量的同时实现竞争对数可能性,反向扩散过程的学习方差允许以更少的正向传递数量级进行采样,而样本质量的差异可以忽略不计,这对于这些模型的实际部署非常重要。 github链接…

AOF:redis宕机,如何避免数据丢失

由于redis是基于内存的数据库,一旦宕机,数据就会丢失?如何解决? 目前,Redis 的持久化主要有两大机制,即 AOF(Append Only File)日志和 RDB(Redis DataBase) 快照。 AO…

SQL零基础入门学习(十四)

上篇:SQL零基础入门学习(十三) SQL NULL 值 NULL 值代表遗漏的未知数据。 默认地,表的列可以存放 NULL 值。 如果表中的某个列是可选的,那么我们可以在不向该列添加值的情况下插入新记录或更新已有的记录。这意味着该…

基于新一代kaldi项目的语音识别应用实例

本文是由郭理勇在第二届SH语音技术研讨会和第七届Kaldi技术交流会上对新一代kaldi项目在学术及“部署”两个方面报告的内容上的整理。如果有误,欢迎指正。 文字整理丨李泱泽 编辑丨语音小管家 喜报:新一代Kaldi团队三篇论文均被语音顶会ICASSP-2023接…

亿级高并发电商项目-- 实战篇 --万达商城项目 十三(编写购物车、优化修改商品、下架商品方法、购物车模块监听修改商品、删除商品消息)

👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者 📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 &#x1f4…

SSL证书对虚拟主机的用处有哪些?

虚拟主机是指在同一台服务器上,通过不同的域名或IP地址为多个网站提供服务的一种网络主机。而SSL证书则是一种数字证书,它用于加密网站与用户之间的通信,确保数据传输的安全性和完整性。在虚拟主机上,SSL证书有以下几个用处&#…

SQL Server2008详细安装步骤(保姆式教程)

安装包下载 链接:https://pan.baidu.com/s/1Rjx4DHJBeCW2asC_4Kzo6Q?pwdchui 提取码:chui 安装过程 1.解压后使用管理员身份打开安装程序 2.选择全新安装或向现有安装添加新功能 3.确认 4.输入产品密钥(上方网盘安装包里有&#xff0…

【路径规划】基于前向动态规划算法在地形上找到最佳路径(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

【ArcGIS Pro二次开发】(10):属性表字段(field)的修改

在ArcGIS Pro中,经常会遇到用字段计算器对要素的属性表进行计算。下面以一个例子演示如何在ArcGIS Pro SDK二次开发中实现。 一、要实现的功能 如上图所示的要素图层,要实现如下功能: 当字段【市级行政区】的值为【泉州市】时,将…

用 .NET 启动你的 DJI Ryze Tello 无人机

大疆的 DJI Ryze Tello 是入门级的无人机,不仅在 STEM 教育中有非常广泛的应用,也可以作为编程入门的首选。通过 UDP 协议调用 DJI Ryze Tello SDK 可以让 DJI Ryze Tello 无人机执行起飞,降落,转向以及不同的花式动作。本文将会通…

Parasoft的自动化测试平台到底强在哪?

在如今产品迭代如此之快的大背景下,软件测试这项工作越来越被大家所重视,但是通常情况下大家都是选择在产品上线前再去做测试,这个时候就会面临很多麻烦和挑战。首先,产品已经开发好之后,体量比较大,要从哪…

BurpSuite配置抓取HTTPS数据包

简介 我们在渗透测试的过程中,经常会遇到HTTPS的网站,Burp默认是没有办法抓取HTTPS的包的,想要让Burp抓取Https的包也很好办,只需要浏览器安装相关的证书即可,接下来将配置过程做一个记录。 前置条件: 1.J…

HashMap原理(一):哈希函数的设计

哈希函数的作用与本质 HashMap用来存储存在映射关系的数据对{key, value},在内部通过构造复合数据结构来封装数据对&#xff0c;即 //伪代码&#xff0c;非源码 class Pair<K, V> {public K key;public V value; }假设用来存储数据对的哈希数表为table&#xff0c;数据…