接口自动化项目实践

先说结论。

覆盖接口:300个。

覆盖模块:12个,其中二级模块47个。

开发模式下,调用接口次数:6828次,脚本运行耗时1h18min3s。

上线模式下,调用接口次数:1257次,脚本运行耗时15min46s。

该系统已稳定运行,本次测试过程中,发现了4个隐藏bug。

此次实践是基于前面分享的几篇博文进行的。

Name Content
测试框架 接口自动化测试框架-AIM
测试思想 结对测试vs随机测试parewise算法性能优化(用例设计层面)
接口工具 F12开发者工具,Postman,Fiddler

编码的难点在于如何总结出各模块各接口的规律,

以尽量简化的代码,尽可能地覆盖更多的场景。

举个例子,下面这两段代码,就是在摸索出代码参数规律后写出来的。

eg1:

        productList = [("1", "3", sample(["5", "6", "7", "8"], 2)),
                       ("1", "4", ["9", "10"]),
                       ("1", "5", sample(["11", "12", "13"], 2)),
                       ("1", "6", ["14", "15"]),
                       ("2", "3", sample(["5", "6", "7", "8"], 2)),
                       ("2", "4", ["9", "10"]),
                       ("2", "7", ["16", "17"])]
        for product in productList:
            for monitorLevel in ("1", "2", "3"):
                self.req({
                    "url": full_url("xxx"),
                    "body": {"xxx": monitorLevel, "xxx": "3", "xxx": product[0],
                             "xxx": product[1],
                             "xxx": product[2]}
                })

eg2:

    def test_portrait(self):
        # xxx
        table = [("1", "xxx"),  # xxx
                 ("2", "xxx"),  # xxx
                 ("3", "xxx"),  # xxx
                 ("4", "xxx"),  # xxx
                 ("5", "xxx"),  # xxx
                 ("6", "xxx"),  # xxx
                 ("7", "xxx"),  # xxx
                 ("8", "xxx")]  # xxx
        data = list()
        for t in table:
            field = [x['COLUMN_NAME'] for x in
                     dao.select_dict('''select * from information_schema.COLUMNS t where table_name=\'%s\';''' % t[1])
                     if x['COLUMN_NAME'] not in ['xxx', 'xxx']]
            data.append({"xxx": t[0], "field_name": field})
        self.req({
            "url": full_url("xxx"),
            "body": {"xxx": pub.xxx(1)[0], "data": data}
        })

参数是接口测试最核心的部分,怎么处理就决定了接口测试的效率。

1、单选/多选

单选

"quarter": ("3", "4", "5", "6")

多选

checkbox("1,2,3,4,6,5")

checkbox是对多选项参数进行了处理:

def checkbox(option, j=None):
    """
    :param option:
    :param j: 组装参数的方式 list无 str默认',' 可以指定
    :return:
    """
    if j is not None:
        if isinstance(option, str):
            option = option.split(j)
        return j.join(option), j.join(sample(option, random.randint(1, len(option) - 1)))
    if j is None:
        if isinstance(option, str):
            j = ','
            option = option.split(j)
            return j.join(option), j.join(sample(option, random.randint(1, len(option) - 1)))
        if isinstance(option, list):
            return option, sample(option, random.randint(1, len(option) - 1))

兼顾了“1,2,3”和[1,2,3]两种组装参数的方式。

2、相互联动的参数

也就是说参数a的变化联动改变参数b。

一种方式是构建元组List,循环遍历

tz_zjbd = [(checkbox("xxx"), ""),  # xxx
                   ("", checkbox("xxx"))]  # xxx
        tz_qixian = [(checkbox("1,2,3,4,5,6"), "", ""),
                     ("", "0", "500")]  # xxx
        relate = [(tz_zjbd[0], tz_qixian[0]),
                  (tz_zjbd[1], tz_qixian[1])]
        pw = list()
        for rel in relate:
            p = {
                "url": None,
                "body": {
                    "content": [
                        {
                            "xxx": "2018-01-01",
                            "xxx": date("2018-01-01", "2019-03-01"),
                            "xxx": "2018-01-01",
                            "xxx": date("2018-01-01", "2019-03-01"),
                            "xxx": checkbox("xxx"),
                            "xxx": "0",
                            "xxx": "100",
                            "xxx": "0",
                            "xxx": "100",
                            "xxx": "0",
                            "xxx": "100",
                            "xxx": checkbox("1,2"),
                            "xxx": checkbox("1,2,3,4,5,6,7"),
                            "xxx": "",
                            "xxx": rel[1][0],
                            "xxx": checkbox("xxx"),
                            "xxx": checkbox("1,2,3,4,5,6"),
                            "xxx": checkbox("1,2,3"),
                            "xxx": ','.join(pub.bankName(3)),
                            "xxx": checkbox("1,2"),
                            "xxx": "理财",
                            "xxx": rel[1][1],
                            "xxx": rel[1][2],
                            "xxx": ','.join(pub.cityName(5)),
                            "xxx": rel[0][0],
                            "xxx": rel[0][1],
                            "xxx": "1",
                            "xxx": "1",
                            "xxx": "1",
                            "xxx": "1",
                            "xxx": "1",
                            "xxx": "1",
                            "xxx": "",
                            "xxx": "asc"
                        }
                    ]
                }
            }
            pw = pw + parewise(p['body']["content"][0])

一种方式是拆开分别处理

p = {
            "url": full_url("xxx"),
            "body": {
                "xxx": checkbox("1,2,3"),
                "xxx": checkbox("1,2"), "xxx": checkbox("1,2,3"),
                "xxx": checkbox("1,2,3,4,6,5"),
                "xxx": checkbox("1,2,3,4,6,5"),
                "xxx": date('2018-01-01', '2018-05-01'),
                "xxx": date('2018-05-02', '2019-01-01'), "xxx": ("1", "2"),
                "xxx": ("area_ch", "all_ch"), "xxx": "", "xxx": ("1", "2")}
        }
        for x in parewise(p['body']):
            p['body'] = x
            self.req(p)
        area_list = sample(
            """'海南省','安徽省','福建省','江西省','山东省','河南省','湖北省','湖南省','广东省','上海','广西壮族自治区',
                '四川省','贵州省','云南省','西藏自治区','陕西省','山西省','甘肃省','青海省','宁夏回族自治区','新疆维吾尔自治区',
                '天津','重庆','内蒙古自治区','辽宁省','吉林省','黑龙江省','江苏省','北京','河北省','浙江省'""".split(','),
            20)
        p = {  # 省份
            "url": full_url("xxx"),
            "body": {
                "xxx": checkbox("1,2,3"),
                "xxx": checkbox("1,2"), "xxx": checkbox("1,2,3"),
                "xxx": checkbox("1,2,3,4,6,5"),
                "xxx": checkbox("1,2,3,4,6,5"),
                "xxx": date('2018-01-01', '2018-05-01'),
                "xxx": date('2018-05-02', '2019-01-01'), "reports_type": ("1", "2"),
                "xxx": "province_ch", "xxx": checkbox(area_list, ","), "xxx": ("1", "2")}
        }
        for x in parewise(p['body']):
            p['body'] = x
            self.req(p)

视情况怎么方便怎么来。

3、接口依赖

接口的参数需要从另外接口的返回数据获取。

r = self.req({
            "url": full_url("xxx"),
            "body": {}
        })
        for content in r.json()['content']:
            for indexList in content['indexList']:
                self.req({
                    "url": full_url("xxx"),
                    "body": {
                        "indexCode": indexList['indexCode'],
                        "searchDate": "year1"
                    }
                })

4、从数据库取测试数据

        s = dao.select_dict("""SELECT * FROM xxx""")
        p = {
            "url": full_url(url),
            "body": {
                "page": 1,
                "pageSize": 15,
                "xxx": tuple(sample([x['doc_issuing_agency'] for x in s], 2)),
                "xxx": tuple(sample([x['doc_name'] for x in s], 2)),
                "xxx": tuple(sample([x['post_no'] for x in s], 2)),
                "xxx": tuple(sample([date2str(x['publish_date']) for x in s], 2)),
                "xxx": tuple(sample([date2str(x['publish_date']) for x in s], 2)),
                "xxx": ""
            }
        }
        for x in parewise(p['body']):
            p['body'] = x
            self.req(p)

5、请求超时的问题,可以参阅这篇频繁请求报requests异常的处理

6、判断flag

最初的方案是每个接口返回flag不一样,在每个接口返回后,在用例中通过将flag和响应,传参给封装函数做判断。

最新的方案是综合所有接口返回结果,在req函数统一判断。用例中不再判断。

    def checkFlag(self, p, r):
        """预期,实际"""
        err = str([p['url'], p['body'], r.text])
        try:
            b = False
            if (r.json()['flag'] in [1, '1', '', None, 'statistic_by_result', 0,
                                     "0", 'struct_product', 'v_select_jz_single']
                    or r.json()['message'] in ("暂无数据", "未查询到数据")):
                b = True
            self.assertEqual(True, b, msg=err)
        except (json.JSONDecodeError, KeyError):  # 1.返回的不是json,比如下载、404  2.无flag
            self.assertEqual(200, r.status_code, msg=err)

说一下心得体会。

做demo和做真实项目完全是两回事。

快捷键是真好用,尤其是代码写多了之后。(虽然写的还不多,但快捷键是真滴)

性能很重要,该优化的要想办法及时优化,哪怕修改实现方式。

码代码前,就算封装一个函数,也要多思考设计,想清楚了再行动,不然挨个挨个重新改一遍真的五味杂陈。

编码效率>框架体验。这是加了用例执行进度1/100这种又去掉之后的感悟。没有必要纠结可有可无的细小体验添加冗余代码。

版权申明:本文为博主原创文章,转载请保留原文链接及作者。

原文地址:https://www.cnblogs.com/df888/p/11747620.html

时间: 2024-11-01 11:30:41

接口自动化项目实践的相关文章

HttpRunner 接口自动化简单实践

1.安装 1.1 命令行pip直接安装就好 1.2 验证安装 命令行输入hrun -V,返回项目版本信息则表明安装成功 2.新建测试项目 这里我用直接通过框架的脚手架工具命令生成目录结构 如:hrun --startproject Api_Test 这样我们就在当前目前目录下建立了一个测试项目Api_Test: 3.一个测试用例demo 用例文件描述.detalk文件描述.测试执行方法描述.测试报告描述(内容.log.参数响应等等)3.1)在Api_Test\testcases目录下新建一个ym

关于接口自动化的那些事 - 基于 Python

网络请求模拟小技巧 在学习了一段时间的Python语言后,咱也大概对Python的语法和逻辑有了一定的积累,接下来,可以为接口自动化测试开始尝试做一些比较简单的准备工作啦~跟着我一起来来来~ 扩展库requests 一般来说接口测试都是基于HTTP和HTTPS的网络请求,Python中有很多自带原生库和扩展库均可以实现.Python模拟HTTP请求有两种方式,一种是使用httplib模块,一种是使用requests模块,我个人比较倾向于使用requests库,该库把请求的框架都搭建好了,使用简洁

2020年第二期《python接口自动化+测试开发》课程,预售!

2020年第二期<python接口自动化+python测试开发>课程,12月15号开学! 主讲老师:上海-悠悠 上课方式:QQ群视频在线教学,方便交流 本期上课时间:12月15号-3月29号,每周六.周日晚上20:30-22:30 报名费:报名费3000一人(周期3个月) 联系QQ:283340479 课表如下 课程主要涉及的内容: 1.fiddler抓包与接口测试(测试基础必备) 2.python+pytest框架实现接口自动化测试(pytest框架甩unittest几条街) 3.httpr

2020年第三期《python接口自动化+测试开发》课程,4月5号开学(火热报名中!)

2020年第三期<python接口自动化+python测试开发>课程,4月5号开学(火热报名中!) 主讲老师:上海-悠悠 上课方式:QQ群视频在线教学,方便交流 本期上课时间:4月5号-6月27号,每周六.周日晚上20:30-22:30 报名费:报名费3000一人(周期3个月) 联系QQ:283340479 课表如下 课程主要涉及的内容: 1.fiddler抓包与接口测试(测试基础必备) 2.python+pytest+allure框架实现接口自动化测试(pytest框架甩unittest几条

接口自动化:HttpClient + TestNG + Java(一) - 接口测试概述+自动化环境搭建

1.1 接口测试简介 1.1.1 什么是接口测试 开始学习接口自动化测试之前,我们先要来了解什么是接口,以及什么是接口测试. 我们都知道,测试从级别上划分可以分为 组件测试 集成测试 系统测试 验收测试 其中在集成测试这个阶段,一个最主要的测试活动就是接口测试.在组件测试中,我们对单个组件自身的功能性能等指标进行验证,上升到集成测试级别,我们则进一步去验证组件之间的交互和集成.而组件之间的交互,就是通过'接口'来达成的.所以一定程度上,集成测试和接口测试概念是大幅度重叠的.(就组件集成/接口测试

[1.30] 保持的力量:接口开发最佳实践

神啊,求你赐给我平静的心,去接受我无法改变的事:赐给我勇气,去做我能改变的事:赐给我智慧,去分辨两者的不同. --平静之祷 1.30.1 论保持的力量 追到一个心仪的女生不难,难于如何保持和培养一份真挚的感情:获得一时的财富也不难,难于如何长久保持收益:创业的公司很容易博得一时媒体的关注以及某次天使的投资,但难于如何排除各种障碍.充分利用各方资源发展成中企业及至上市公司. 同样,提供一时的接口很容易,但当我们需要不断为接口提供升级,以及当我们维护提供一整套接口时,面临的困难和问题会越来越大.所以

接口自动化实战

废话少说,直接上接口自动化的实战. 需要的材料: 1.开发提供接口文档,如果没有接口文档,只要有URL也行,给大家普及下URL基础知识,这对做接口测试很重要. 例如:URL:https://i.cnblogs.com/EditPosts.aspx?opt=1,这个路径中要访问的网页是/EditPosts.aspx,i.cnblogs.com则是它的域名,?号后面指要提交的参数,具体是get还是post提交请大家用firebug抓取看就知道了,?号后如果有多个参数,参数与参数之间用"&&q

php后台对接ios,安卓,API接口设计和实践完全攻略,涨薪必备技能

2016年12月29日13:45:27 关于接口设计要说的东西很多,可能写一个系列都可以,vsd图都得画很多张,但是由于个人时间和精力有限,所有有些东西后面再补充 说道接口设计第一反应就是restful api 请明白一点,这个只是设计指导思想,也就是设计风格 ,比如你需要遵循这些原则 原则条件REST 指的是一组架构约束条件和原则.满足这些约束条件和原则的应用程序或设计就是 RESTful.Web 应用程序最重要的 REST 原则是,客户端和服务器之间的交互在请求之间是无状态的.从客户端到服务

Hangfire项目实践

Hangfire项目实践分享 Hangfire项目实践分享 目录 Hangfire项目实践分享 目录 什么是Hangfire Hangfire基础 基于队列的任务处理(Fire-and-forget jobs) 延迟任务执行(Delayed jobs) 定时任务执行(Recurring jobs) 延续性任务执行(Continuations) 与quartz.net对比 Hangfire扩展 Hangfire Dashborad日志查看 Hangfire Dashborad授权 IOC容器之Au