接口自动化测试的最佳工程实践 (ApiTestEngine)

news2024/11/24 1:46:56

目录

前言:

背景

核心特性

特性拆解介绍

写在后面


前言:

接口自动化测试是现代软件开发中不可或缺的一环。一个良好的测试框架和最佳工程实践可以提高测试效率和质量。

背景

当前市面上存在的接口测试工具已经非常多,常见的如PostmanJMeterRobotFramework等,相信大多数测试人员都有使用过,至少从接触到的大多数简历的描述上看是这样的。除了这些成熟的工具,也有很多有一定技术能力的测试(开发)人员自行开发了一些接口测试框架,质量也是参差不齐。

但是,当我打算在项目组中推行接口自动化测试时,搜罗了一圈,也没有找到一款特别满意的工具或框架,总是与理想中的构想存在一定的差距。

那么理想中的接口自动化测试框架应该是怎样的呢?

测试工具(框架)脱离业务使用场景都是耍流氓!所以我们不妨先来看下日常工作中的一些常见场景。

  • 测试或开发人员在定位问题的时候,想调用某个接口查看其是否响应正常;
  • 测试人员在手工测试某个功能点的时候,需要一个订单号,而这个订单号可以通过顺序调用多个接口实现下单流程;
  • 测试人员在开始版本功能测试之前,可以先检测下系统的所有接口是否工作正常,确保接口正常后再开始手工测试;
  • 开发人员在提交代码前需要检测下新代码是否对系统的已有接口产生影响;
  • 项目组需要每天定时检测下测试环境所有接口的工作情况,确保当天的提交代码没有对主干分支的代码造成破坏;
  • 项目组需要定时(30 分钟)检测下生产环境所有接口的工作情况,以便及时发现生产环境服务不可用的情况;
  • 项目组需要不定期对核心业务场景进行性能测试,期望能减少人力投入,直接复用接口测试中的工作成果。

可以看到,以上罗列的场景大家应该都很熟悉,这都是我们在日常工作中经常需要去做的事情。但是在没有一款合适工具的情况下,效率往往十分低下,或者就是某些重要工作压根就没有开展,例如接口回归测试、线上接口监控等。

先说下最简单的手工调用接口测试。可能有人会说,Postman就可以满足需求啊。的确,Postman作为一款通用的接口测试工具,它可以构造接口请求,查看接口响应,从这个层面上来说,它是满足了接口测试的功能需求。但是在具体的项目中,使用Postman并不是那么高效。

不妨举个最常见的例子。

某个接口的请求参数非常多,并且接口请求要求有MD5签名校验;签名的方式为在 Headers 中包含一个sign参数,该参数值通过对URLMethodBody的拼接字符串进行MD5计算后得到。

回想下我们要对这个接口进行测试时是怎么做的。首先,我们需要先参照接口文档的描述,手工填写完所有接口参数;然后,按照签名校验方式,对所有参数值进行拼接得到一个字符串,在另一个 MD5 计算工具计算得到其 MD5 值,将签名值填入sign参数;最后,才是发起接口请求,查看接口响应,并人工检测响应是否正常。最坑爹的是,我们每次需要调用这个接口的时候,以上工作就得重新来一遍。这样的实际结果是,面对参数较多或者需要签名验证的接口时,测试人员可能会选择忽略不进行接口测试。

除了单个接口的调用,很多时候我们也需要组合多个接口进行调用。例如测试人员在测试物流系统时,经常需要一个特定组合条件下生成的订单号。而由于订单号关联的业务较多,很难直接在数据库中生成,因此当前业务测试人员普遍采取的做法,就是每次需要订单号时模拟下单流程,顺序调用多个相应的接口来生成需要的订单号。可以想象,在手工调用单个接口都如此麻烦的情况下,每次都要手工调用多个接口会有多么的费时费力。

再说下接口自动化调用测试。这一块儿大多接口测试框架都支持,普遍的做法就是通过代码编写接口测试用例,或者采用数据驱动的方式,然后在支持命令行(CLI)调用的情况下,就可以结合Jenkins或者crontab实现持续集成,或者定时接口监控的功能。

思路是没有问题的,问题在于实际项目中的推动落实情况。要说自动化测试用例最靠谱的维护方式,还是直接通过代码编写测试用例,可靠且不失灵活性,这也是很多经历过惨痛教训的老手的感悟,甚至网络上还出现了一些反测试框架的言论。但问题在于项目中的测试人员并不是都会写代码,也不是对其强制要求就能马上学会的。这种情况下,要想在具体项目中推动接口自动化测试就很难,就算我可以帮忙写一部分,但是很多时候接口测试用例也是要结合业务逻辑场景的,我也的确是没法在这方面投入太多时间,毕竟对接的项目实在太多。所以也是基于这类原因,很多测试框架提倡采用数据驱动的方式,将业务测试用例和执行代码分离。不过由于很多时候业务场景比较复杂,大多数框架测试用例模板引擎的表达能力不足,很难采用简洁的方式对测试场景进行描述,从而也没法很好地得到推广使用。

可以列举的问题还有很多,这些也的确都是在互联网企业的日常测试工作中真实存在的痛点。

基于以上背景,我产生了开发ApiTestEngine的想法。

对于ApiTestEngine的定位,与其说它是一个工具或框架,它更多的应该是一套接口自动化测试的最佳工程实践,而简洁优雅实用应该是它最核心的特点。

当然,每位工程师对最佳工程实践的理念或多或少都会存在一些差异,也希望大家能多多交流,在思维的碰撞中共同进步。

核心特性

ApiTestEngine的核心特性概述如下:

  • 支持 API 接口的多种请求方法,包括 GET/POST/HEAD/PUT/DELETE 等
  • 测试用例与代码分离,测试用例维护方式简洁优雅,支持YAML
  • 测试用例描述方式具有表现力,可采用简洁的方式描述输入参数和预期输出结果
  • 接口测试用例具有可复用性,便于创建复杂测试场景
  • 测试执行方式简单灵活,支持单接口调用测试、批量接口调用测试、定时任务执行测试
  • 测试结果统计报告简洁清晰,附带详尽日志记录,包括接口请求耗时、请求响应数据等
  • 身兼多职,同时实现接口管理、接口自动化测试、接口性能测试(结合 Locust)
  • 具有可扩展性,便于扩展实现 Web 平台化

特性拆解介绍

支持 API 接口的多种请求方法,包括 GET/POST/HEAD/PUT/DELETE 等

个人偏好,编程语言选择 Python。而采用 Python 实现 HTTP 请求,最好的方式就是采用Requests库了,简洁优雅,功能强大。

测试用例与代码分离,测试用例维护方式简洁优雅,支持YAML

要实现测试用例与代码的分离,最好的做法就是做一个测试用例加载引擎和一个测试用例执行引擎,这也是之前在做AppiumBooster框架的时候总结出来的最优雅的实现方式。当然,这里需要事先对测试用例制定一个标准的数据结构规范,作为测试用例加载引擎和测试用例执行引擎的桥梁。

需要说明的是,测试用例数据结构必须包含接口测试用例完备的信息要素,包括接口请求的信息内容(URL、Headers、Method 等参数),以及预期的接口请求响应结果(StatusCode、ResponseHeaders、ResponseContent)。

这样做的好处在于,不管测试用例采用什么形式进行描述(YAML、JSON、CSV、Excel、XML 等),也不管测试用例是否采用了业务分层的组织思想,只要在测试用例加载引擎中实现对应的转换器,都可以将业务测试用例转换为标准的测试用例数据结构。而对于测试用例执行引擎而言,它无需关注测试用例的具体描述形式,只需要从标准的测试用例数据结构中获取到测试用例信息要素,包括接口请求信息和预期接口响应信息,然后构造并发起 HTTP 请求,再将 HTTP 请求的响应结果与预期结果进行对比判断即可。

至于为什么明确说明支持YAML,这是因为个人认为这是最佳的测试用例描述方式,表达简洁不累赘,同时也能包含非常丰富的信息。当然,这只是个人喜好,如果喜欢采用别的方式,只需要扩展实现对应的转换器即可。

测试用例描述方式具有表现力,可采用简洁的方式描述输入参数和预期输出结果

​测试用例与框架代码分离以后,对业务逻辑测试场景的描述重任就落在测试用例上了。比如我们选择采用YAML来描述测试用例,那么我们就应该能在YAML中描述各种复杂的业务场景。

那么怎么理解这个 “表现力” 呢?

简单的参数值传参应该都容易理解,我们举几个相对复杂但又比较常见的例子。

  • 接口请求参数中要包含当前的时间戳;
  • 接口请求参数中要包含一个 16 位的随机字符串;
  • 接口请求参数中包含签名校验,需要对多个请求参数进行拼接后取 md5 值;
  • 接口响应头(Headers)中要包含一个X-ATE-V头域,并且需要判断该值是否大于 100;
  • 接口响应结果中包含一个字符串,需要校验字符串中是否包含 10 位长度的订单号;
  • 接口响应结果为一个多层嵌套的 json 结构体,需要判断某一层的某一个元素值是否为 True。

可以看出,以上几个例子都是没法直接在测试用例里面描述参数值的。如果是采用 Python 脚本来编写测试用例还好解决,只需要通过 Python 函数实现即可。但是现在测试用例和框架代码分离了,我们没法在YAML里面执行 Python 函数,这该怎么办呢?

答案就是,定义函数转义符,实现自定义模板。

这种做法其实也不难理解,也算是模板语言通用的方式。例如,我们将${}定义为转义符,那么在{}内的内容就不再当做是普通的字符串,而应该转义为变量值,或者执行函数得到实际结果。当然,这个需要我们在测试用例执行引擎进行适配实现,最简单方式就是提取出${}中的字符串,通过eval计算得到表达式的值。如果要实现更复杂的功能,我们也可以将接口测试中常用的一些功能封装为一套关键字,然后在编写测试用例的时候使用这些关键字。

接口测试用例具有可复用性,便于创建复杂测试场景

很多情况下,系统的接口都是有业务逻辑关联的。例如,要请求调用登录接口,需要先请求获取验证码的接口,然后在登录请求中带上获取到的验证码;而要请求数据查询的接口,又要在请求参数中包含登录接口返回的 session 值。这个时候,我们如果针对每一个要测的业务逻辑,都单独描述要请求的接口,那么就会造成大量的重复描述,测试用例的维护也十分臃肿。


比较好的做法是,将每一个接口调用单独封装为一条测试用例,然后在描述业务测试场景时,选择对应的接口,按照顺序拼接为业务场景测试用例,就像搭积木一般。如果你之前读过AppiumBooster的介绍,应该还会联想到,我们可以将常用的功能组成模块用例集,然后就可以在更高的层面对模块用例集进行组装,实现更复杂的测试场景。

不过,这里有一个非常关键的问题需要解决,就是如何在接口测试用例之前传参的问题。其实实现起来也不复杂,我们可以在接口请求响应结果中指定一个变量名,然后将接口返回关键值提取出来后赋值给那个变量;然后在其它接口请求参数中,传入这个${变量名}即可。

测试执行方式简单灵活,支持单接口调用测试、批量接口调用测试、定时任务执行测试

通过背景中的例子可以看出,需要使用接口测试工具的场景很多,除了定时地对所有接口进行自动化测试检测外,很多时候在手工测试的时候也需要采用接口测试工具进行辅助,也就是半手工+半自动化的模式。

而业务测试人员在使用测试工具的时候,遇到的最大问题在于除了需要关注业务功能本身,还需要花费很多时间去处理技术实现细节上的东西,例如签名校验这类情况,而且往往后者在重复操作中占用的时间更多。

这个问题的确是没法避免的,毕竟不同系统的接口千差万别,不可能存在一款工具可以自动处理所有情况。但是我们可以尝试将接口的技术细节实现和业务参数进行拆分,让业务测试人员只需要关注业务参数部分。

具体地,我们可以针对每一个接口配置一个模板,将其中与业务功能无关的参数以及技术细节封装起来,例如签名校验、时间戳、随机值等,而与业务功能相关的参数配置为可传参的模式。

这样做的好处在于,与业务功能无关的参数以及技术细节我们只需要封装配置一次,而且这个工作可以由开发人员或者测试开发人员来实现,减轻业务测试人员的压力;接口模板配置好后,测试人员只需要关注与业务相关的参数即可,结合业务测试用例,就可以在接口模板的基础上很方便地配置生成多个接口测试用例。

测试结果统计报告简洁清晰,附带详尽日志记录,包括接口请求耗时、请求响应数据等

测试结果统计报告,应该遵循简洁而不简单的原则。“简洁”,是因为大多数时候我们只需要在最短的时间内判断所有接口是否运行正常即可。而 “不简单”,是因为当存在执行失败的测试用例时,我们期望能获得接口测试时尽可能详细的数据,包括测试时间、请求参数、响应内容、接口响应耗时等。

​之前在读locust源码时,其对HTTP客户端的封装方式给我留下了深刻的印象。它采用的做法是,继承requests.Session类,在子类HttpSession中重写覆盖了request方法,然后在request方法中对requests.Session.request进行了一层封装。

request_meta = {}

# set up pre_request hook for attaching meta data to the request object
request_meta["method"] = method
request_meta["start_time"] = time.time()

response = self._send_request_safe_mode(method, url, **kwargs)

# record the consumed time
request_meta["response_time"] = int((time.time() - request_meta["start_time"]) * 1000)

request_meta["content_size"] = int(response.headers.get("content-length") or 0)

​而HttpLocust的每一个虚拟用户(client)都是一个HttpSession实例,这样每次在执行HTTP请求的时候,既可充分利用Requests库的强大功能,同时也能将请求的响应时间、响应体大小等原始性能数据进行保存,实现可谓十分优雅。

受到该处启发,要保存接口的详细请求响应数据也可采用同样的方式。例如,要保存ResponseHeadersBody只需要增加如下两行代码:

request_meta["response_headers"] = response.headers
request_meta["response_content"] = response.content

身兼多职,同时实现接口管理、接口自动化测试、接口性能测试(结合 Locust)

其实像接口性能测试这样的需求,不应该算到接口自动化测试框架的职责范围之内。但是在实际项目中需求就是这样,又要做接口自动化测试,又要做接口性能测试,而且还不想同时维护两套代码。

多亏有了locust性能测试框架,接口自动化和性能测试脚本还真能合二为一。

前面也讲了,HttpLocust的每一个虚拟用户(client)都是一个HttpSession实例,而HttpSession又继承自requests.Session类,所以HttpLocust的每一个虚拟用户(client)也是requests.Session类的实例。

同样的,我们在用Requests库做接口测试时,请求客户端其实也是requests.Session类的实例,只是我们通常用的是requests的简化用法。

以下两种用法是等价的。

resp = requests.get('http://debugtalk.com')

# 等价于
client = requests.Session()
resp = client.get('http://debugtalk.com')

有了这一层关系以后,要在接口自动化测试和性能测试之间切换就很容易了。在接口测试框架内,可以通过如下方式初始化HTTP客户端。

def __init__(self, origin, kwargs, http_client_session=None):
   self.http_client_session = http_client_session or requests.Session()

默认情况下,http_client_sessionrequests.Session的实例,用于进行接口测试;当需要进行性能测试时,只需要传入locustHttpSession实例即可。

具有可扩展性,便于扩展实现 Web 平台化

当要将测试平台推广至更广阔的用户群体(例如产品经理、运营人员)时,对框架实现 Web 化就在所难免了。在 Web 平台上查看接口测试用例运行情况、对接口模块进行配置、对接口测试用例进行管理,的确会便捷很多。

不过对于接口测试框架来说,Web平台只能算作锦上添花的功能。我们在初期可以优先实现命令行(CLI)调用方式,规范好数据存储结构,后期再结合 Web 框架(如 Flask)增加实现 Web 平台功能。

  作为一位过来人也是希望大家少走一些弯路

在这里我给大家分享一些自动化测试前进之路的必须品,希望能对你带来帮助。

(WEB自动化测试、app自动化测试、接口自动化测试、持续集成、自动化测试开发、大厂面试真题、简历模板等等)

相信能使你更好的进步!

点击下方小卡片

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

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

相关文章

建筑结构健康监测常见问题及解决措施

建筑结构健康监测(SHM)是指利用无损传感技术,通过对结构特性进行分析,达到检测结构损伤或退化的目的,是当前守护建筑安全的一种新型技术手段,通过建筑结构健康监测管理者可以直观的了解到建筑物的健康状态,为建筑维护和…

【力扣算法13】之 12. 整数转罗马数字 python

文章目录 问题描述示例1示例2 示例 3:示例 4:示例 5:提示 思路分析代码分析完整代码详细分析运行效果截图调用示例运行结果 完结 问题描述 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 字符数值I1V5X…

AP5193 DC-DC宽电压LED降压恒流驱动器 LED电源驱动IC

产品 AP5193是一款PWM工作模式、外围简单、内置功率MOS管,适用于4.5-100V输入的高精度降压LED恒流驱动芯片。电流2.5A。AP5193可实现线性调光和PWM调光,线性调光脚有效电压范围0.55-2.6V.AP5193 工作频率可以通过RT 外部电阻编程来设定,同时…

centos环境搭建nsq集群

简言 1. nsq是go语言开发的实时的分布式消息处理平台,目的是用来大规模地处理每天数以十亿计级别的消息。它具有分布式和去中心化拓扑结构,该结构具有无单点故障、故障容错、高可用性以及能够保证消息的可靠传递的特征 2. nsq官网地址 NSQ Docs 1.2.1 …

【详解】C语言冷门知识点之--位段

文章目录 一, 位段的解释二, 位段的声明和使用位段的声明:位段的使用: 三,位段的空间大小计算第一个例子:第二个例子:注意: 四, 位段的内存分配五,位段的跨平…

@Autowired 到底是怎么把变量注入进来的?

文章目录 1. Bean 的创建2. populateBean3. postProcessProperties3.1 findAutowiringMetadata3.2 inject3.3 doResolveDependency 4. 时序图 在 Spring 容器中,当我们想给某一个属性注入值的时候,有多种不同的方式,例如可以通过构造器注入、…

C++ string类型的基本使用方法

目录 1.定义和初始化string对象 2.string对象上的常用操作 在C中string是用来处理可变长字符串的,是C标准库中提供的类型,使用起来十分方便。同时C也支持C语言的字符数组来表示字符串。使用时记得包含string头文件。 1.定义和初始化string对象&#xf…

讨论下相亲交友小程序介绍红娘系统搭建的功能有哪些

首页内容 同城会员,VIP会员,线下会员,热文推荐,恋爱话术,爱情故事,恋爱宝典,相亲宝典,浪漫约会,相亲活动,地区、年龄筛选,用户动态,用…

(简单)剑指Offer || 056. 二叉搜索树中两个节点的和 Java

方法一:深度优先搜索哈希表 使用深度优先搜索的方式遍历整棵树,用哈希表记录遍历过的节点的值 对于一个值为x的节点,检查哈希表中是否存在k-x即可。如果存在对应的元素,那么我们就可以在该树上找到两个节点的和为k;否…

Idea 修改默认 Maven 为自己的

每次我们打开新项目时,都要去配置一遍 maven,很麻烦,其实可以去修改 idea 里面默认的 maven 配置,这样后面不管是打开新项目还是老项目,就都是用的自己的 maven 了. 1.文件->新项目设置->新项目的设置 File->Other Settings -> Settings for New Project 2.然后和…

【Unity开发必备】100多个 Unity 学习网址 资源 收藏整理大全【持续更新】

Unity 相关网站整理大全 众所周知,工欲善其事必先利其器,有一个好的工具可以让我们事半功倍,有一个好用的网站更是如此! 但是好用的网站真的太多了,收藏夹都满满的(但是几乎没打开用过😁)。 所以本文是对…

eclipse : sun.misc.BASE64Encoder找不到jar包的解决方法

sun.misc.BASE64Encoder找不到jar包 比较好的解决办法 按顺序依次操作: Windows -> Preferences -> Java -> Compiler -> Errors/Warnings。再按照顺序依次: Deprecated and trstricted API -> Forbidden reference (access rules): -&g…

量子力学的实验验证:双缝实验和贝尔不等式

亲爱的读者, 欢迎回到我们的量子力学系列文章。在前几篇文章中,我们介绍了量子力学的起源、基本概念,以及叠加态、超级定位和量子纠缠等奇特现象。今天,我们将深入探讨量子力学的实验验证,重点介绍双缝实验和贝尔不等…

Android自定义圆环进度条/刻度仪表盘(单环单点带进度动画)

效果图: 1.自定义SleepDashBoardView /*** 睡眠刻度仪表盘*/ public class SleepDashBoardView extends View {private static final float START_ANGLE 135f;private static final float MAX_ANGLE 270f;private float progress 0;private float centerX;private float ce…

ValueError: check_hostname requires server_hostname怎么解决?

背景: 想使用pip安装某一个包。结果报错如上图绿框所示。 解决方法: 把代理(梯子)关掉就行了。

数据库应用:MySQL事务、存储引擎

目录 一、理论 1.事务 2.MySQL 存储引擎 二、实验 1.事务隔离级别 2.事务控制语句 三、总结 一、理论 1.事务 (1)概念 ① 事务是一种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系…

XUbuntu22.04之解决ThindPad P15V风扇狂转的问题(一百八十三)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

深度学习标量、向量、矩阵、张量之间的区别与联系

文章目录 前言1、张量**注意**: 2、**标量** (scalar):0阶的张量,0个轴,一个单独的数(整数或实数);3、**向量**(vector):1阶的张量,也叫矢量,1个轴,一个数组;…

elasticsearch学习入门+实战

学习链接1 基础概念 官网学习:地址 基本命令 PS:使用Apifox测试 查询所有索引库 添加索引库 添加时,加入分词器 添加时,加入记录属性值 查询获取索引库 删除索引库 添加文档 必须要在添加文档值的时候用【_doc】&a…

如何建立统一的自动化测试平台?

前面的文章中我们为大家介绍了中通科技自动化测试当时正在面临的一些困境。第一个是自动化测试框架太多,测试工程师在选择框架和脚本语言的时候很难统一,脚本编写门槛高。第二个是运行脚本的平台不统一,脚本运行时不够稳定。第三个是不同的Je…