探讨Morest在RESTful API测试的行业实践
摘要:在本文中,我们将重点探讨使用自动化智能化Morest测试技术在RESTful API测试的行业实践。
本文分享自华为云社区《【智能化测试专题】华为云API智能测试工具--Morest测试框架》,作者: DevAI 。
如今,许多公司都在通过RESTful API提供云服务。随着RESTful API的日益普及,测试RESTful API变得至关重要。为了解决这个问题,研究人员提出了几种自动RESTful API测试技术。在华为,我们设计并实现了一个名为Morest的自动化RESTful API测试框架。Morest已用于测试10个微服务,并检测出83个未被发现的程序错误,这些错误均已被开发人员确认并修复。尽管,在微服务的RESTFUL API的测试方面,Morest展现出强大的测试用例生成能力和缺陷探知能力,但同时,我们也注意到在实践中应用自动RESTful API技术时,开发者的参与是不可避免的并且重要。
在本文中,我们将重点探讨使用自动化智能化Morest测试技术在RESTful API测试的行业实践。
1 简介
REpresentational state transfer (REST) 的概念由Roy Fielding在2000年首次提出[1]。它是一种软件架构风格,定义了不同组件之间交互的约束。在Web服务的上下文中,符合REST约束的API称为RESTful API。使用RESTful API,可以将状态以JSON、XML、YAML等格式传输到客户端。在过去的十年中,各种公司(如亚马逊、谷歌、华为和微软)采用REST标准来设计他们的Web API。
随着RESTful API的出现,测试RESTful API服务对于Web服务的质量保证变得至关重要。为此,已经提出了几种技术[2-4]来自动生成RESTful API的测试用例。在华为,我们基于我们之前的工作[4]开发了一个高度自动化和弹性的RESTful API测试框架,称为Morest。Morest曾在华为测试了十个RESTful API服务,并展示了强大的能力,因为它帮助揭示了总共83个以前未知的错误。所有错误都得到了开发人员的确认并已得到修复。虽然Morest可以减轻手动编写RESTful API测试用例的负担,但我们发现开发者在实践中仍然发挥着重要作用。在本文中,我们关注自动RESTful API测试中的人为因素。
首先,我们介绍了Morest框架的设计,并解释了这种设计如何有助于促进Morest和开发者之间的更好协调。其次,我们将Morest的性能与几个基线进行了比较,包括由华为内部服务的质量保证团队维护的手动编写的测试套件,以研究Morest如何优于其他自动测试技术,以及Morest如何补充人工。最后,我们讨论了在应用Morest过程中的经验教训。
调查结果/要点可总结为:
? Morest 在华为的10个微服务中发现了83个以前未识别到的漏洞,所有这些漏洞都得到了开发者的确认和修复。这显示了在行业实践中应用自动RESTful API测试工具的巨大潜力。
? 即使测试过程高度自动化,人为因素对于RESTful API测试也很重要。首先,OpenAPI 规范的质量对于自动化工具的性能至关重要,这些规范必须手动维护和管理。其次,生成回归测试用例需要适当的设置和拆卸机制,由于复杂性高,无法自动处理。
? 由人类专家和自动化工具创建的测试用例可以相互补充。尤其是手动创建的测试用例擅长成功请求 RESTful API,而自动创建的测试用例擅长覆盖角落用例。因此,提高测试性能需要人类专家和自动化工具之间的良好协调。
? 为了促进专家和工具之间的顺利协调,工具输出的呈现方式也很重要。根据我们对来自不同产品的81位华为开发者的调查,Morest 测试报告中最需要的四个属性是状态码、失败的测试用例、成功的测试用例以及参数分布。
2 Morest的框架
2.1 概述
图1显示了Morest的概述。一般来说,Morest包含三个组件:测试用例生成、测试用例执行和测试报告生成。Morest的输入一般是被测服务的符合OpenAPI规范的接口定义文档,输出是包含回归测试用例和分类故障以进行调试的测试报告。
2.2 测试用例生成
测试用例生成是Morest的核心组件。它是基于我们之前的工作[4]构建的。我们不讨论每个技术细节,而是总结测试用例生成的三个关键步骤。
首先,Morest从目标服务的OpenAPI规范中提取API之间的依赖关系。图3显示了针对 5个API的YAML格式的S-Service的OpenAPI规范。我们可以看到该规范嵌入了不同 API 之间的依赖关系。
例如,在图 3 中,操作 getVolume 以参数 storage_id 作为输入,它是操作 getStorage 的输出。这表明 getVolume 依赖于 getStorage。 Morest 通过参数推断 [4] 提取此类依赖关系。由于 Morest 依赖 OpenAPI 规范作为唯一输入,因此 OpenAPI 规范的质量至关重要。
第二,Morest 使用提取的 API 之间的依赖关系生成 RESTful-service Property Graph (RPG)。RPG 可以描述详细的 API 依赖关系,并允许在测试过程中动态改进捕获的依赖关系1。图 2 显示了图 3 中规范的相应 RPG。在 RPG 中,每个节点代表一个操作或模式,边代表操作和模式之间的数据依赖关系。例如,一条从操作 A 指向模式 B 的边意味着 A 的至少一个参数来自模式 B。
第三,Morest 从 RPG 生成 API 调用序列来测试目标服务。通过以深度优先的方式遍历 RPG,Morest 可以生成请求序列作为不同使用场景的测试用例。
2.3 测试用例执行
如图 1 所示,我们使用测试用例执行器来执行 2.2 节生成的测试用例。测试执行者负责具体化调用序列。一般来说,Morest中的参数有两种,即具体值和依赖值。
具体来说,Morest 会根据 OpenAPI 规范的定义生成具体值。另一方面,从 API 的先前响应中检索到的依赖值在执行期间动态组装。一旦执行了具体的测试用例,执行器将相应的响应转发给测试用例生成部分和测试报告生成部分。这里要解决的一个关键挑战是如何构建一个高吞吐量的测试用例执行器。由于可能有多个实例为不同的 RESTful API 服务生成测试用例,我们实现了一个 Kafka 集群来并行执行测试用例。因此,我们可以通过动态增加 Kafka 集群的能力来增加吞吐量。
2.4 测试报告生成
Morest 的测试报告包括用于不同目的的成功测试用例和失败测试用例。
2.4.1 成功的测试用例(回归测试)
只有当它的所有API调用都收到带有2xx状态代码的响应时,测试用例才被认为是成功的。成功的测试用例被聚合并作为回归测试用例持续存在,以持续测试RESTful API服务。由于RESTful API的频繁更新,编写回归测试用例需要不平凡的手动工作。通过将 Morest 集成到生产管道中,Morest可以减轻开发人员的负担。这里值得一提的是,应该应用两个过程(设置和拆除)来组装最终的回归测试用例。为了引导回归测试,setup 用于初始化可执行环境(例如,在图3中创建存储)。另一方面,由于测试用例通常不是幂等的(换句话说,它们可能会意外地改变RESTful API服务的状态,从而导致执行失败),因此设计拆解是为了补偿受影响的状态(例如,释放测试用例执行后创建的存储)。目前,Morest使用CRUD创建、读取、更新和删除语义[5]来自动组装回归测试用例。或者,我们还在管道中启用手动制作的回归测试用例。
2.4.2 失败的测试用例(检测到的失败)
Morest通常在一小时内检测到1,000多个故障,并且验证每个故障是劳动密集型的。因此,在这个组件中,我们通过状态码、RESTful API端点和相应的测试用例序列AURORA [6] 应用于集群故障,以补偿开发人员发现缺陷的努力。此外,还可以将失败的测试用例修改后添加为回归测试用例,不断提高回归测试用例的健壮性和多样性。
3 评估
为了评估 Morest 的性能,我们在华为内部的一个云服务上进行了183个RESTful API的实验,即S-Service。我们使用了四个基线来与Morest进行比较:
? 随机生成 (RandomGen):API调用序列以及API参数是随机生成的。
? 自下而上的生成(Restler):Restler从单个RESTful API开始,并通过根据生产者-消费者关系动态包含更多 API 调用来逐步生成测试用例。
? 操作依赖图引导生成:(RestTestGen):与Morest一样,RestTestGen[3]使用OpenAPI规范推断RESTful API之间的生产者-消费者关系。它构建并遍历操作依赖图(ODG)以生成API调用序列。与ODG相比,RPG承载的信息更多,可以动态更新。
? 手动编写的测试套件(ManualSuite):我们获取了由S-Service的质量保证(QA)团队维护的测试用例,用于研究手动编写的测试用例与自动生成的测试用例之间的关系。对于自动RESTful API测试技术,我们运行了8个小时,以便它们能够收敛。我们还重复了五次实验以减轻随机性。对于ManualSuite,我们将所有测试用例运行一次,因为这组测试用例是固定的。
为了评估不同技术的性能,我们收集了在 S-Service 上检测到的行覆盖率、方法覆盖率和独特错误的数量。每种评估技术的代码覆盖率和检测到的错误的平均结果如表 1 所示。
我们可以观察到 ManualSuite 实现了最高的代码覆盖率。通过审查ManualSuite中的测试用例并与S-Service的QA团队讨论,我们发现人类专家对S-Service的领域知识比任何自动生成技术都多,尤其是在OpenAPI规范没有很好记录的情况下。因此,人类专家编写的测试用例总是可以成功请求目标RESTful API,而工具生成的测试用例可能无法请求某些API。同时,我们也注意到ManualSuite中的测试用例主要是为了回归目的而维护的。因此,ManualSuite不会揭示S-Service中的任何错误。
最后,但同样重要的是,从表1中,我们还可以看到,由于其测试用例生成引擎[4],Morest在自动化技术中实现了最高性能。此外,Morest可以发现其他工具检测到的所有错误。
为了全面了解自动化测试技术和手动编写的测试套件之间的差距,我们进一步研究了每个自动化工具和ManualSuite之间的代码覆盖率差异,结果如表2所示。
从表2可以看出,虽然ManualSuite可以达到最佳的整体代码覆盖率,但即使是随机生成的测试用例,平均也能覆盖ManualSuite无法覆盖的120.40行代码。结果表明,自动化工具确实可以补充人类的努力。通过进一步研究代码覆盖率报告,我们发现自动化工具擅长覆盖极端情况。事实上,手动编写的测试用例并不能详尽地涵盖API调用的不同组合,更不用说API参数的不同值。
此外,覆盖极端案例还有助于自动化工具检测错误。例如,Morest可以为volume_id参数生成一个包含64个字母的字符串来触发解析错误。
4 经验教训
4.1 OpenApi规范的质量
RESTful API 服务的OpenAPI规范由开发者维护,是Morest学习正确API使用的唯一数据源。因此,OpenAPI规范的质量会极大地影响Morest的性能。为规范接口文档质量,华为提出了严格的OpenAPI规范要求和检测工具。但是,一些遗留项目的RESTful API规范,例如S-Service,并没有遵循标准。
在实践中,我们发现OpenAPI规范的质量不佳是Morest代码覆盖率的主要限制。
在这里,我们总结了阻碍OpenAPI规范质量的两个最常见的问题,并提供了处理它们的策略。
4.1.1 重复参数名
由于 Morest 依赖参数名称来推断 RESTful API 之间的依赖关系,因此重复的参数名称会带来混乱。如图 4 所示,getVolumeMapping 和 getVolumeHost 都需要输入id。
但是,两个端点中名为 id 的参数指的是不同的实体(一个id代表VolumeMapping,另一个标识VolumeHost)。
虽然RPG的设计和动态更新策略可以缓解这个问题[4],但 Morest 仍然不能完全摆脱这个问题。因此,Morest可能会生成无效的测试用例,从而限制测试效率。为了应对这一挑战,最直接的方法是重新设计相应的被测系统,并确保特定实体的参数名称具有唯一性。然而,这是劳动密集型的且不切实际的。由于可能会在被测服务之上构建各种其他服务,因此更改参数名称可能会导致意外行为。因此,在实践中,我们建议使用名为x-name [7] 的OpenAPI 扩展来重命名具有相同名称的参数。Morest可以通过查询规范中的x-name来区分它们。
4.1.2 生产环境相关问题
被测服务在不同的生产环境中可能表现不同。具体来说,连接到不同设备的生产环境可能会对API参数产生不同的限制。例如,在图5中,capability的值受生产环境的连接设备数量的限制。如果capability的值无效,相关的测试用例也会失效。为了填补RESTful API规范和生产环境需求之间的空白,我们建议为每个生产环境单独生成一个规范。
4.2 高并发测试
在这里,我们将讨论我们使 Morest 框架具有弹性和效率的经验。我们利用 Kafka 集群来执行测试用例并以异步方式同时检索相关响应。此外,大多数RESTful API服务允许异步API调用以实现高吞吐量。但是,这带来了维护API正确调用序列的挑战。例如,如果我们调度一个任务在S-Service中创建一个存储并立即附加卷到它,那么下面的操作很可能会失败,因为在S-Service中创建一个存储需要几秒钟才能完成。在实践中,我们通过设置定制的异步RESTful API处理程序(例如,为相关API插入小的时间延迟)来解决这一挑战。
4.3 测试输出的呈现
4.3.1 测试报告
Morest 可以在8小时内生成超过 200,000 个测试用例(在第3节的实验中)。分析测试结果是劳动密集型的。因此,我们对华为的81位开发者进行了调查,以确定哪些项目应该包含在报告中。
图 6 显示了开发人员最想要的四个属性。特别是,所有开发人员都要求提供状态代码和失败的测试用例,以定位其 RESTful API 服务背后的故障。超过一半的开发人员(85.2% = 69 / 81)希望包含成功的测试用例,可以用作回归测试用例。此外,大量开发人员 (70.4% = 57 / 81) 关心参数分布(n向组合覆盖率)以评估特定轮次测试是否足够。
4.3.2 故障聚类
在将所有捕获的故障呈现给用户之前,Morest使用聚类算法(例如AURORA[6])对失败的测试用例进行分组。根据测试目标(例如,缺陷搜寻、性能评估),可以相应地应用不同的分组标准(例如,状态代码、错误消息和响应时间)。我们的主要经验是提供通用的聚类标准,以满足不同开发或质量保证团队的需求
4.3.3 回归测试
Morest的一个关键特性是生成回归测试用例。由于RESTful API服务的复杂性,生成的测试用例通常不是幂等的。因此,我们需要设置和拆卸组件来组装回归测试用例。
否则,简单地应用自动生成的测试用例可能会污染RESTful API环境(例如,创建脏数据)并导致误报失败(例如,由于不同的系统状态而失败)。我们的经验是,生成设置和拆卸代码是逐案生成的,并且需要有关被测服务的强大领域知识。因此,构建回归测试用例需要人类专家和Morest之间的协调。
5 相关工作
一些现有的工作专注于自动测试 RESTful API。RestTestGen[3]利用输入OpenAPI规范的静态分析来生成和执行测试用例。具体来说,RestTestGen中请求RESTful API的顺序是固定的。Restler[2]从请求单个RESTful API开始,并形成受给定测试用例长度阈值限制的测试用例。相比之下,Morest使用定制的测试用例生成引擎,该引擎通过静态分析生成初始测试用例,并通过动态参数推断改进测试用例。此外,Morest是一个用于测试RESTful API服务的可扩展框架。Restler和RestTestGen都可以作为测试用例生成引擎集成到Morest中。
EvoMaster[8]以白盒方式测试RESTful API。不幸的是,在大多数情况下,我们无法获得RESTful API服务的源代码。即使可以访问源代码,也需要付出很多努力来设置环境和编写测试驱动程序。因此,我们选择了黑盒方式来执行自动化的RESTful API测试。
最近,一项实证研究[9]发现自动RESTful API测试存在各种未解决的问题。在这项工作中,我们将Morest应用于测试商业RESTful API服务,这为可扩展的自动化RESTful API测试提供了启示。
6 结论
在本文中,我们介绍了Morest,一个用于自动测试RESTful API服务的框架。Morest具有自动化程度高、基于场景的测试用例生成、高并发测试用例执行等特点。Morest被用于华为10个微服务的测试,发现了83个未被发现的Bug。通过在实践中应用Morest,我们发现,尽管Morest具有强大的错误探知能力,但仍然需要人工来准备符合OpenAPI规范的接口文档以及构建回归测试用例。此外,通过对华为内部微服务的测试实验,我们发现自动化工具可以通过覆盖边界情况来补充人工编写测试用例的不足。
原文链接: 点击关注,第一时间了解华为云新鲜技术~