接口测试 个人互联网接口自动化遇到的问题回顾

流浪豆 · 2019年02月07日 · 最后由 黑山老妖 回复于 2019年02月15日 · 5945 次阅读

内容:

        介绍本人在过往的互联网行业所做的接口自动化测试遇到的坑来进行抛砖引玉。

目的:

        希望大家看了本文后,能够把自己的经验以及遇到的坑也都分享出来, 好的大家一起学习,深坑大家一起填平。👏👏欢迎大家吐槽和分享经验。

从文章的标题表达的两个点:

互联网

在互联网行业工作的小伙伴应该知道互联网行业的 "特性"(个人观点,勿喜勿喷)

  • 需求迭代频繁, 需求更新频繁
  • 测试压力较大,开发:测试人员比例失调
  • 业务变更频繁,可能某个项目上线后 3-5 个月就被淘汰了。
  • 在如此频繁的需求变更以及迭代压力下,代码在多个开发的"蹂躏"下,可能已经破烂不堪,急需重构

可能还有其他的就不一一列举了。

接口测试

        在这里不详细的介绍怎么做接口测试以及为什么要做接口测试了, 看了一些社区里面的老文章,在这里直接引用下。
        接口测试,想说爱你 “很” 容易
        接口测试的一些感悟

个人接口自动化测试过程中遇到的坑

(1)互联网 + 接口测试的用例设计

        到底应该怎么去设计接口自动化用例,个人觉得这是一个大坑。。。
        一个比较常见的场景,网上购物或者消费时候经常会使用优惠券,这时候在下单的时候就必然存在券核销这一块。假设存在一个券核销接口 consumeCoupon

  public Boolean consumeCoupon(CouponRequest couponRequest)

  public class CouponRequest{
    public Long memberId;       // 会员Id 
    public Long shopId;         // 消费店铺Id
    public String couponId;     // 优惠券Id
    public String orderId;      // 订单Id
    ...
}

        从接口测试,想说爱你 “很” 容易文章中,列举了怎么设计接口测试用例。如果严格按照里面的用例设计进行用例编写,这个接口的用例量可想而知,这接口的入参还仅仅是只有 4 个字段的简单对象。在互联网行业的做过接口自动化测试的小伙伴应该也深有体会,每次测试任务也不会有充足的时间去进行这块的用例编写。

个人解决思路(我觉得不是太好的解决方案)

在这种场景下,我一般是把接口按照以下的几个维度进行划分:

  • 接口调用方:

    • 外部接口:接口提供给非本公司的其他业务方使用。
    • 内部接口:接口提供给公司内部的其他业务使用。
  • 接口核心程度: 核心接口 or 非核心接口

  • 接口入参复杂度:

    • 简单入参:入参数是 JAVA 的基本类型(int、string 等)和简单的自定义对象(少于 4 个基本类型字段,如上面的 CouponRequest )。
    • 复杂入参:入参是复杂对象(对象中包括大于 4 个以上的基本类型字段或者包含自定义的子对象)

      public class Request{
          public Long memberId;           // 会员Id 
          public Long shopId;             // 消费店铺Id
          public String couponId;         // 优惠券Id
          public CouponInfo couponInfo;   // 优惠券详情
          public String orderId;          // 订单Id
          ...
      }
      

针对上面提及的内部接口、核心接口、简单入参接口会进行更加全面的用例编写。
从功能方面进行校验以及部分业务异常场景的覆盖,比如:对于一个正确的 couponRequest 请求体,对其中某个字段用其他数据替换再进行接口调用,具体如下:

  1. 会员 Id A --> 会员 Id a
  2. 店铺 Id B --> 店铺 Id b
  3. 优惠券 Id C --> 优惠券 Id c
  4. 订单 Id D --> 订单 Id d
  5. 等等。。。

从代码层面, 可以通过阅读开发的代码,根据圈复杂度来进行用例编写。使用过这种办法一段时间,发现代码经过多任开发的 "蹂躏",每个人的理解程度不一样导致代码的层次参差不齐,设计的用例覆盖场景不全面。(个人不太推荐使用该方法)

待解决的问题:这样用例量以及时间花费仍然很大,在实际项目中测试人员没有充足的时间来进行用例编写,那么怎么即不降低用例的覆盖场景,又能快速编写用例呢??对于这个问题,等后续有解决方案后再持续跟新。

(2)服务的核心程度以及生命周期

        确定服务的核心程度以及生命周期!生命周期!生命周期!!!

        重要的事情说 3 遍。。。个人血淋淋的经验,在新入公司后, 找了个团队内的项目进行接口自动化用例编写,等完成了 50% 后,开发说需要进行代码重构, 这个项目全部废弃。 😢😢当时真是几万个草泥马在内心奔腾。
        所以在选择具体对什么项目开始接口自动化一定需要和开发团队进行细致的规划,确保需要做接口自动化的服务是核心服务以及服务的生命周期足够长

(3)新项目的接口测试

        新项目在接口定义时候,由于产品、开发或者测试对于需求的理解不充分,场景考虑不周全的情况下,接口变化以及新增接口会比较频繁的。所以需要测试与开发人员每天及时沟通接口定义,确保用例是按照最终的接口定义进行编写的。

(4)接口数据的构造

        这应该也是做接口自动化的另外一个大坑,对于复杂对象的数据构造。
        最初的时候,找开发直接提供或者是在业务代码里面通过 java 的 log 组件打印出每个接口的入参。但这种办法对于业务代码不是太友好,里面全都是 log 代码,并且修改工作量过大。
        后续通过了解 AOP 编程,直接在代码里面添加自己的 AOP 代码织入来完成业务代码的入参日志打印功能,也可以将日志进行分析存储到数据库中进行后续使用。

(5)接口自动化的价值

        当一件事完成后,大家当然想能够看到一定的成效,但是接口自动化这种东西。在某种程度上来说可能是费力不讨好。现在互联网公司在测试自动化这块越来越普及,但得到的效果却不尽人意。
        测试是为了发现软件中的 BUG,部分人也就觉得接口自动化只有在发现了多少个 BUG,才能体现出其价值。可能这套接口自动化用例每天都运行,但是却未发现任何的 BUG,这时候不能马上否定其作用。个人觉得应该从多层面来进行判断其价值,

  • 用例覆盖的接口代码每日变化量(需要对业务逻辑进行了一定的修改,而不是一些日志内容修改),代码没有实际业务逻辑的改动,自动化用例理所当然也不会存在收益。
  • 接口用例的断言是否合适, 很多时候的用例断言可能只是校验返回是否是成功就结束了,这样的用例价值也几乎可以忽略不计。
  • 接口的代码覆盖度。
  • 接口的功能通过人工回归的时间。

    个人对有效的接口自动化理解:

            代码存在每日变化量,接口断言覆盖了所有检测点,接口的代码行覆盖度大于 50%~70%(这个值根据团队的项目代码量以及核心程度来判断),如果在这种情况下,所有用例每次都是执行通过,不能说该接口自动化没有效果,只能证明你所在的团队开发非常给力!!!

            最后, 在满足以上的条件下,计算 ROI = 接口人工回归的时间/编写接口自动化用例以及维护框架的时间,ROI 越大表示接口自动化用例的效果越好。

(6)接口自动化框架

        框架这块我就不多说了,个人用的是 requests+ unintest + htmlTestRunner

总结

       什么是专家??就是把某块领域大家知道的坑都自己填过一次、多次或知道怎么填。我在自动化测试这条路上还有点远。。。如果大家有什么好的解决方案可以在评论区反馈🙏🙏

共收到 14 条回复 时间 点赞

新项目的接口测试,可以通过抓包的方式自动构造接口

haleli 回复

这里的抓包方式指的是??tcpdump、wireshark? 能否具体描述下相关的关键点或者资料参考下呢🙏

可以从流程上去推动一些规范,例如接口的规范、接口文档的参数列表和说明,接口设计等等,减少接口频繁变动带来的风险和自动化维护成本

ui 已经被吐槽了,投入多收益少;
然后接口也被吐槽了,投入多收益少,太麻烦;
然后还能干啥? 都成为战略专家(战术专家都不成) 么

hellohell 回复

对于吐槽这种东西吧!个人觉得可以有,毕竟不是量身定做的。
1、吐槽了表示在自己用了,然后用的时候用的不爽了,不爽了那就需要来解决,于是就看看大家有没有什么好的思路。
2、如果仅仅是吐槽,而不尝试着去解决问题。那才属于无病呻吟。

Jerry li 回复

嗯嗯,流程上,我们通过使用格式化的接口定义来控制,在自动化代码里面再根据格式化的接口定义自动生成请求的测试代码。至于接口设计这块取决于开发和测试对接口的理解了,这块我们还需要加强。

这样用例量以及时间花费仍然很大,在实际项目中测试人员没有充足的时间来进行用例编写,那么去即不降低用例的覆盖场景,又能快速编写用例呢??

最近也有遇到类似的问题,暂时解决办法是提测前先覆盖冒烟场景,非核心场景有时间且有必要重复执行,就补充编写自动化用例,否则半自动化(在已有代码基础上手动改参数)。

思路上,可以试试基于接口 API 文档(引入 swagger 注解可以自动生成)自动按照数据类型,通过参数组合生成测试用例,对于单接口参数构造方面的场景用例编写会有一定程度的效率提升。

陈恒捷 回复

🙏 🙏 多谢提议, 😂 才发现 自己问题里面句子打错字。。

自动按照数据类型,通过参数组合生成测试用例

我通过写了个脚本对接口文件分析来生成用例, 但是只是生成一个简单的 case,自动构建出 post 请求的 datas、headers, 具体参数还需要人工编写 test ,不过这样也可以快速构建出用例,但对于参数组合这块还是没太想到怎么自动组合:

public CouponDTO queryMemberCoupons(QueryRequest request);

# 通过接口文档解析出来, 生成的固定格式service。
    query_member_coupons_data = {
        'url': 'xxx',
        'method': 'queryMemberCoupons',
        'paramTypes': ['com.xxx.QueryRequest'],
    }

    def init_query_member_coupons_data(self, request):
        args = {'paramValues': [json.dumps(request)]}
        append_data = self.query_member_coupons_data
        datas = CommonMethod.init_data(self.temp_data, append_data, args)
        return datas

    def query_member_coupons(self, request):
        data = self.init_query_member_coupons_data(request)
        data = json.dumps(data)
        result, code = HttpOperate.http_post(url=self.url, headers=self.headers, data=data)
        return result

# 再自动生成一个服务CouponUtil
    @classmethod
    def query_member_coupons(cls, request):
        result = cls.coupon_member_service.query_member_coupons(request)
        return result

# test 人工编写
    def test_query_member_coupons_case_1(self):
        member_request = CouponFactory.build_data(xxx)

        # test
        test_result = CouponUtil.query_member_coupons(member_request)

代码和用例分离,请求体可以通过字段 + 参数的组合方式,方便灵活拼接。
用例存放在本地数据库,由例标题、主要参数的值、预期值等组成,非紧要参数可以直接放在代码中或直接作为某个字段的值。
对 response 验证,除接口返回值外,还有对数据库验证。

陈恒捷 回复

参数组合生成用例后,对应的断言有没有方法,可以不用一个个单独编写啊😂

例如写好了自动化用例,跑了第一遍后,第二遍执行时候,发现有部分用例执行失败了!例如使用优惠券时,这个优惠券的 id,第一遍执行时候已经用掉,第二次使用肯定不可以。这时候该怎么处理呢?1.该用例数据;2 改数据库,每次执行用例前初始化数据库下。但是有些数据还可能存在缓存,队列里,这样如果初始化的脚本,要写一大堆,尤其是这样的用例很多的时候。这种情况下大家都怎么处理啊

黑山老妖 回复

这种可以通过前置条件来处理吧,封装通用的前置条件,比如 新增/获取 特定优惠券等。
但是这种条件多了,维护起来也麻烦。。😂

黑山老妖 回复

1、思路上可以考虑在参数组合的表格里手动加一列,作为断言。然后根据这个列来生成对应的断言
2、你说的这个优惠券不能重复跑场景,个人觉得核心点是把造数据和测试用例分开。造数据保证每次都给出一个可用的数据(如未使用的优惠券 id),用例就直接用数据好了,而且这个造数据还可以提供给非自动化测试时用。
整体初始化数据库这种方法,对于单体应用可用,但对于涉及多个服务的应用(每个应用有自己的缓存、数据库)就不行了。

陈恒捷 回复

了解,多谢

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册