极光小课堂|手把手教你做接口测试

接口测试是项目测试过程中非常重要的一环,测试的对象是接口,所以提早介入测试,对代码逻辑进行全面验证,就会更早的发现程序的问题。同时,接口测试比UI测试效率更高,并且更容易验证极端和异常的情况。

那么什么是接口测试,以及如何在实际环境中进行接口测试呢?本文手把手教会你接口测试的逻辑和方法。

1.接口测试概念

1.1接口的定义

应用程序编程接口(API Application Programming Interface)是在不同进程,程序或系统之间的通信的代码.API通常基于客户机/服务器模型。有交互的地方都有接口。最常见的跨应用交互协议是HTTP + JSON,其次是网络服务。最常见的后台协议有thrift,dubbo及大量的私有协议。

1.2接口测试的定义

接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。简答的说就是通过URL向服务器或者其他模块等,传输我们想传输的数据,然后看看它们返回的是不是我们预期想要的。

1.3接口测试的重要性

  • 越底层发现错误,它的修复成本是越低的。
  • 前端随便变,接口测好了,后端不用变,前后端是两拨人开发的。
  • 检查系统的安全性,稳定性,前端传参不可信,比如京东购物,前端价格不可能传入-1元,但是通过接口可以传入-1元。
  • 如今的系统复杂度不断上升,传统的测试方法成本急剧增加且测试效率大幅下降,接口测试可以提供这种情况下的解决方案。
  • 接口测试相对容易实现自动化持续集成,且相对UI自动化也比较稳定,可以减少人工回归测试人力成本与时间,缩短测试周期,支持后端快速发版需求。接口持续集成是为什么能低成本高收益的根源。
  • 现在很多系统前后端架构是分离的,从安全层面来说:(1),只依赖前端进行限制已经完全不能满足系统的安全要求(绕过前面实在太容易),需要后端同样进行控制,在这种情况下就需要从接口层面进行验证。(2)中,前后端传输,日志打印等信息是否加密传输也是需要验证的,特别是涉及到用户的隐私信息,如身份证,银行卡等。

总之接口测试是保证高复杂性系统质量的内在要求和低成本的经济利益的驱动作用下的最佳解决方案,接口测试是一个完整的体系,也包括功能测试,性能测试。

2.怎么做接口测试

2.1接口测试的准备工作

1)是否有接口文档?接口文档内容有哪些?

o 接口文档五要素:接口地址,接口请求的方式,是否有请求参数(参数相关属性),返回参数说明(参数相关属性),返回结果样例;

o 如果没有接口文档,到功能测试阶段,需要自己抓包,抓包工具如提琴手等。

2)明确接口测试流程。

o 主要的流程包括:接口文档 - 接口测试计划,方案 - 接口测试用例(评审) - 执行 - 集成到Jenkins - 接口反馈

3)分析每一个接口:标题,URL,参数(含义,可选/必选,格式,类型等等),响应数据来源及数据量。

4)分析实际可做接口测试的测试点。

o 单一接口功能的测试主要测试返回的数据结构是否和接口文档给出的一致;

o 接口的正常功能是否完成;

o 接口的参数检查测试,接口的异常测试;

o 多接口组合测试,实际上是在测试一个业务流;

o 在测试过程中一次调用多个接口。

2.2编写测试用例

编写一个覆盖率全面的用例,我们可以考虑以下的测试点:

一般我们能把上面的测试点都覆盖了,用例就差不多了。

2.3接口测试过程

根据开发人员提供的接口访问地址,入参格式,请求格式,进行接口请求数据拼接,并查看返回结果及返回报文,响应时间,检查返回的Json内容是否符合接口定义规范,是否符合预期的返回结果。

  • 总的来说可以划分为三个部分:

o 发起接口请求(请求)

o 解析接口响应(响应)

o 检查接口测试结果(确认)

2.4接口测试方法

如何通过接口向服务器发送请求数据,并接收返回的数据呢一般我们用以下的测试工具?

  • WebService的接口:是用皂协议包装后用HTTP运输,请求报文和返回报文都是XML格式的,我们在测试的时候都用通过工具才能进行调用,测试 可以使用的工具有了SoapUI, JMeter的,LoadRunner的等
  • Http api接口:直接用http协议运输,通过路径来区分调用的方法,请求报文都是键值形式的,返回报文一般都是json串,有得和后等方法,这也是最常用的两种请求方式。 可以使用的工具有邮差,RESTClient实现,的JMeter,LoadRunner的等

。当然我们也可以自编接口测试脚本在测试脚本过程中,应注意:

  • 不断调试脚本;
  • 添加逻辑控制,对脚本内的数据进行参数化(包括:前置条件,测试步骤及测试数据);
  • 添加用例里的预期结果。

最后,执行测试和脚本,并对执行结果进行分析包括:错误分析,响应结果分析,响应时间分析等等。

3.自动化

3.1自动化框架选择

市面上有许多接口自动化的框架,我们这次讲的是我所用的一套接口自动化框架。

python+requests+robotframework

选取robotframework主要原因是:

  • 使用关键字的机制,更容易上手
  • 提供了RIDE,对于不熟悉编码的人来说比较友好
  • 能够精细的控制关键字的范围
  • 日志和报告非常好
  • 使用变量文件的机制来描述不同的环境
  • 丰富的关键字库
  • 内置变量

3.2框架搭建

蟒蛇和请求库的安装在此就不累赘了,我们介绍robotframework的安装,由于最近RIDE1.7.3的版本改进,RIDE这个版本对高版本的wxPython的兼容很好,python3.6及以上版本都可以顺利运行。RIDE所以我们可以采用在线安装robotframework:

pip install robotframework #在线安装RF,安装好了输入robot命令验证一下,结果报错显示缺少参数则说明安装成功
pip install robotframework-ride #在线安装RIDE,相关wxpython等相关依赖会自行安装的,安装成功后在python安装目录下Scripts目录里面有一个ride.py文件,双击运行它,如下:


这样我们就把框架搭建好了。

3.3方法的封装

由于不同公司,不同开发者的接口规范不一样,所以我们一般会用蟒二次封装一些常用方法,使之更适合我们请求接口,获取返回值;方便后续再robotframework上编写自动化测试用例常见的二次封装有如下例子:

  • 请求方法和返回数据的封装,可以参考以下代码:
    #api请求方法封装,method 枚举值为:POST、GET、PUT、DELETE
    def Senddata(self, url, data, method):
        print ('*************** Senddata 函数开始 ***************', method)
            if method == "POST":
                print('url:', url)
                if data == "nodata":
                    resp = requests.post(url, data=commApi.data, headers=commApi.headers, timeout=30, verify=False)
                else:
                    datastr = eval(data)
                    resp = requests.post(url, data=json.dumps(datastr), headers=commApi.headers, timeout=30,
                                         verify=False)
            elif method == "PUT":
                if data == "nodata":
                    resp = requests.put(url, data=commApi.data, headers=commApi.headers, timeout=30, verify=False)
                else:
                    datastr = eval(data)
                    resp = requests.put(url, data=json.dumps(datastr), headers=commApi.headers, timeout=30, verify=False)
                commApi.res = resp.json()
            elif method == "DELETE":
                resp = requests.delete(url, headers=commApi.headers, timeout=30, verify=False)
                commApi.res = resp  # 删求成功无数据返回,无需要json转码请求结果
                if int(resp.status_code) != 200:
                    commApi.res = resp.json()  # 删求失败有数据返回,需要json转码请求结果
            else:
                if data == "nodata":
                    resp = requests.get(url, headers=commApi.headers, timeout=30, verify=False)
                else:
                    datastr = eval(data)
                    resp = requests.get(url, params=datastr, headers=commApi.headers, timeout=40, verify=False)
                commApi.res = resp.json()
            commApi.code = resp.status_code
            resp.raise_for_status()  # 如果响应状态码不是 200,就主动抛出异常
        except requests.RequestException as e:
            print(e)
            return commApi.res
        else:
            return commApi.res
    #设置请求头
    def setHeader(self,account=None,password=None):
        print('*************** setHeader 函数开始 ***************')
        if account == None and password == None:
            commApi.headers = {'Content-Type': 'application/json'}
        else:
            Authorization = 'Basic %s:%s'%(account,password)
            print ('Authorization:',Authorization)
            commApi.headers = {'Content-Type': 'application/json', 'Authorization': Authorization}
    #获取接口返回数据(json格式)
    def getRes(self):
        print('*************** getRes 函数开始 ***************')
        return commApi.res
    #检查状态返回码
    def Check_code(self, code='200'):
        print('*************** Check_code 函数开始 ***************')
        assert (commApi.code == int(code))
    #检查接口返回数据
    def Check_response(self, result, code, message=None, checkData=None):
        print('*************** Check_response 函数开始 ***************')
        backdata = commApi.res
        if result == "success":
            assert (backdata[u'code'] == int(code))
        elif result == "failed":
            assert (backdata[u'message'] == message and backdata[u'code'] == int(code))
        return backdata
  • 获取随机数据方法的封装,可以参考如下封装方式:

3.4接口框架设计 - 数据与接口分离

分层原则:【分层目标:接口数据和接口业务分离】

可再细分为:

1. 项目接口分层 :具体要实施接口自动化的工品的主目录。其中,项目主目录中,按分层思想,又可根据接口功能作为二级目录。

目录结构规范:
模块名:project name
子模块名:folder name
接口名:suite name