基于Python3接口自动化测试从0到测试方案落地

作者: 测试架构师 分类: 求知,软件测试 发布时间: 2019-10-22 11:43

希望这篇文章可以

  • 帮助有一点代码基础的测试伙伴独立动手搭建一套完整、高效、可读性好、便于维护的接口自动化系统
  • 帮助给出一些在自动化过程中因环境,业务,甚至人员配合等原因造成阻力的解决方案
  • 让大家拍砖,帮助我更好的对它进行优化和扩展

自卖自夸

  • 目前这套系统已维护一家中大型互联网企业核心交易线服务1年半时间,累计用例数量2万条,覆盖率在85%以上,质量保障效果显著
  • 在业务项目中不断演进优化,在脚本输出效率,代码覆盖率以及应对常见的开发设计模式都有较好的表现(非平台)
  • 开发都觉得好才是真的好!!!

一套东西不可能应对所有情况,但也希望对大家有所帮助,废话不多数,开始。


认识接口

当我们做任何新产品测试时候,第一件事情就是要先认识被测体,接口测试也一样。

什么是接口?

接口的标准定义这里就不赘述了,那我们通常测试的接口是什么?举个例子,当我们点击打开一个商品页面时,客户端需要调用==商品详情接口==,目的是希望通过这个接口告诉服务端商品的ID(即唯一标识)并要求服务端返回这个商品的详细信息,这就是我们最长提到的接口。

接口的组成?

我们知道接口有很多概念包括:http接口、api接口、RPC接口、RMI、webservice、Restful等,下面以我们最常见的http接口为例,如果我们把一个接口看成一个对象那么它主要包含如下属性:

class RequestUtil(object):
    """
    接口基础类
    """
    def __init__(self):
        self.host = None  # 域名:http://my-api-test.com
        self.uri = None  # /mydemo
        self.url = None  
        # url host+uri+param
        self.info = None  # 接口描述
        self.method = None  # 请求方式 post get put等等
        self.body = None 
        # 请求参数体 例如 
        {"product_id": 123456}
        self.param = None  
        # get请求参数 例如 product_id=123456
        self.resp = None  
        # 返回结果,例如 
        {"product_name":'abc',"price":5.11}
        self.is_sign = None  # 签名 
        self.key_token = None  # token
        self.headers = headers 
        # 请求头 例如 
        {'Content-Type': 'application/json'}
        self.status = 0  # 期望的接口返回结果中代表失败、成功状值
        self.success_status = 0  # 接口成功时的status值
        self.http_status = 200  # http返回码
        self.timeout = 15  # 最大超时时间
        self.mock_info = BaseObj()  # 如果接口中需要mock的话,存放mock信息

    def send_request():
        """发送请求,使用requests库来实现该功能"""
        pass

可以看到上面已经生成的我们接口最基础的类,包含了我们后续需要用到的所有属性,并且它还应该包含一个重要的功能就是组织好这些属性然后发送请求并记录请求的所有数据,这里使用的是requests库来实现这个功能。

如何获取接口结构?

通常我们会根据每个公司不同的情况按照优先级从高到低排序有如下获取方式:

推动开发提供以上文档推荐直接接swagger,可以要求开发将参数是否必填和描述等信息写在会用到的参数后面,以此来弥补没有wiki的部分,这样我们解析拉出的脚本中参数就都有具体的描述信息了,非常方便,如下图中内容就是直接从swagger页面解析的脚本

# 以下代码从swagger拉取生成,可从swagger重新拉取更新

from common.objects import BaseObj
from ..__init__ import *


class MyServiceBaseDemoApi(BaseServiceDemo):
    """接口对象"""
    def __init__(self, **kwargs):
        super(MyServiceBaseDemoApi, self).__init__()
        self.info = "my demo接口例子"
        self.uri = "mydemo"
        self.method = "post"
        self.body = self.Body(**kwargs)
        self.resp = self.Resp()

    class Body(BaseObj):
        def __init__(self, **kwargs):
            self.name = None  # 必填,用户姓名
            self.age = None  # 非必填,用户年龄
            self.phone_num = None  # 必填,用户手机号码 必填,用户手机号码
            BaseObj.__init__(self, **kwargs)

    class Resp(object):
        def __init__(self):
            super(MyServiceBaseDemoApi.Resp, self).__init__()
            self.status = None  # None
            self.msg = None  # 公司ID
            self.data = self.Items()  # 公司ID

        class Items(object):
            def __init__(self):
                self.name = None
                self.age = None

熟悉被测服务

除了认识单个的接口,我们还需要了解被测服务的架构以及该服务是如何和其他服务或客户端进行数据交换来完成产品的部分功能,举个例子:在交易流程中通常会包括以下几个服务

这快信息可以直接从开发那边获取,通常开发会有类似的业务架构图,只有了解了这快我们才可以比较准确的来制定相关项目的测试方案,避免漏测,错测。

熟悉一门语言

目前我使用的是python3,当然你们也可以选择java或者其他合适的语言,虽然语言不同,但很多思路和解决问题的方式都是互通的;以python为例,应用要求至少能熟悉到python对象的处理,具体语法这里就不赘述了,百度一下很多,自己比较推荐廖雪峰的python教程,因为比较容易入手。

了解相关依赖库

后面会主要用到以下依赖库,可以提前了解下基本的功能和调用方式

peewee>=2.10.2  # 数据库orm
peewee-mssql>=0.1.2
pymysql  # mysql
redis>=2.10.6  # mock部分的key会存储在redis中
requests>=2.18.4  # http请求的发送,也可以使用aiohttp或其他类似库替换
selenium>=3.8.0  # 接口和ui自动化结合时候可能会需要用到
threadpool>=1.3.2  # 业务并发
pytest=4.3.1  # 自动化测试框架,也可以选择其他类似框架
pytest-html  # pytest生成报告插件
parameterized  # 参数化用例需要使用到

常见接口测试方案

抛开业务,广义上我们主要用到两种测试方案

方向 方案
用例方向 1.单接口测试;2.多接口串流测试
断言部分 1.新老服务对比结果;2.根据业务逻辑计算期望结果和实际结果做对比;

用例方面:

  • 推荐大部分接口尽量都需要有单接口或短流程的测试用例;
  • 流程型较强的业务例如拼团等,必须要加入全流程串流测试;

结果检查:

  • 重构,分库分表等其他不改变业务逻辑的技术型项目非常适合新老服务接口对比测试。==优势==:脚本无需自行生成期望结果,只需对应新老结果即可,脚本量大幅度减少;期望结果可靠性非常高;
  • 根据实际的业务逻辑来自行生成接口返回结果or落库数据的期望结果,一个接口仅包含一个check方法。==优势==: 维护成本稳定,不会随着用例的大幅增多而激增;可实现数据的全量对比,大幅度提高代码覆盖率。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注