探讨 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 规范的接口文档以及构建回归测试用例。此外,通过对华为内部微服务的测试实验,我们发现自动化工具可以通过覆盖边界情况来补充人工编写测试用例的不足。
REFERENCES
[1] R. T. Fielding and R. N. Taylor, “Architectural styles and the design of network-based software architectures,” Ph.D. dissertation, 2000,aAI9980887.
[2] V. A, P. G, and M. P, “Restler: stateful REST API fuzzing,” in ICSE 2019. IEEE / ACM, 2019, pp. 748–758.
[3] E. V, M. D, and M., “Resttestgen: Automated black-box testing of restful apis,” in ICST 2020, oct 2020.
[4] Y. Liu, Y. Li, G. Deng, Y. Liu, R. Wan, R. Wu, D. Ji, S. Xu, and M. Bao, “Morest: Model-based restful API testing with execution feedback,” CoRR, vol. abs/2204.12148, 2022. [Online]. Available: https://doi.org/10.48550/arXiv.2204.12148
[5] I. Rauf and I. Porres, “Beyond CRUD,” in REST: From Research to Practice,E. Wilde and C. Pautasso, Eds. Springer, 2011, pp. 117–135. [Online]. Available: https://doi.org/10.1007/978-1-4419-8303-9_5
[6] T. Blazytko, M. Schlögel, C. Aschermann, A. Abbasi, J. Frank, S. Wörner, and T. Holz, “AURORA: statistical crash analysis for automated root cause explanation,” in 29th USENIX Security Symposium, USENIX Security 2020, August 12-14, 2020, S. Capkun and F. Roesner, Eds. USENIX Association, 2020, pp. 235–252. [Online]. Available: https:// www.usenix.org/conference/usenixsecurity20/presentation/blazytko
[7] OpenAPI, “Openapi extensions,” https://swagger.io/docs/specification/openapi-extensions/, 2022-06-01 2022.
[8] A. Arcuri, “Evomaster: Evolutionary multi-context automated system test generation,” in 11th IEEE International Conference on Software Testing, Verification and Validation, ICST 2018, Västerås, Sweden, April 9-13, 2018. IEEE Computer Society, 2018, pp. 394–397. [Online]. Available: https://doi.org/10.1109/ICST.2018.00046
[9] M. Kim, Q. Xin, S. Sinha, and A. Orso, “Automated test generationfor REST apis: No time to rest yet,” CoRR, vol. abs/2204.08348, 2022. [Online]. Available: https://doi.org/10.48550/arXiv.2204.08348
版权声明: 本文为 InfoQ 作者【华为云开发者联盟】的原创文章。
原文链接:【http://xie.infoq.cn/article/7a3a0b50e2e820eaa2073d501】。文章转载请联系作者。
评论