写点什么

手把手教你编写 HTTP 文件测试 HTTP API

  • 2020-06-09
  • 本文字数:7426 字

    阅读完需:约 24 分钟

手把手教你编写HTTP文件测试HTTP API


本文最初发布于 Renato Athaydes 的个人网站,经原作者授权由 InfoQ 中文站翻译并分享。


目前,为 HTTP API 编写自动化测试实在有点太复杂了。一般来说,你可以通过两种方法来解决这个问题:


  • 使用一个 HTTP 客户端用你喜欢的编程语言编写测试。

  • 使用 Postman、Swagger Inspector 和老一些的 SoapUI 等 GUI 工具。


我认为这些方法都不够理想。进一步来说,我认为这里有另一种解决方案,它在大多数情况下都好用得多,但知道的人却很少:那就是直接在一个文本文件中编写 HTTP,同时添加一些人性化的便利内容,带来更愉快的测试体验。


在这篇文章中,我会阐述当前方法中存在哪些问题,以及为什么 HTTP 文件是一个很好的替代方案。

编写 HTTP API 测试时面临的问题

用你喜欢的编程语言编写测试时,你有无限的灵活性。基本上,你想到什么就能做什么,因此就灵活性而言,程序化测试绝对是最佳选择。


但这同时也是程序化测试的问题所在:由于它们太通用了,因此代码很难阅读和维护。如果测试足够复杂,甚至连程序要测试的是什么东西都很难搞清楚。


这是很常见的状况,而且往往会带来一个问题:测试程序本身由谁来测试呢?你能保证测试失败是由 API 中的错误引起的吗?如果失败是因为测试程序中的错误引起的呢?


程序化测试也很难编写。由于某些原因,大多数语言的 HTTP 客户端都比较笨拙,难以使用。举个简单的例子,下面是大多数 Java 开发人员都必须处理的事情:


class GitHubUser {     private String login;     // standard getters and setters}
public class MyTest { @Test public void givenUserExists_whenUserInformationIsRetrieved_thenRetrievedResourceIsCorrect() throws ClientProtocolException, IOException { // Given HttpUriRequest request = new HttpGet( "https://api.github.com/users/eugenp" ); // When HttpResponse response = HttpClientBuilder.create().build().execute( request ); // Then GitHubUser resource = RetrieveUtil.retrieveResourceFromResponse( response, GitHubUser.class); assertThat( "eugenp", Matchers.is( resource.getLogin() ) ); }
public static <T> T retrieveResourceFromResponse(HttpResponse response, Class<T> clazz) throws IOException { String jsonFromResponse = EntityUtils.toString(response.getEntity()); ObjectMapper mapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return mapper.readValue(jsonFromResponse, clazz); }
}
复制代码


上面的例子来自Baeldung网站


也许你认为这看起来还行,但我可不这么认为。我希望后文的分析能让你明白其实还有好得多的方法。

基于 GUI 的测试工具

基于 GUI 的测试工具所面临的问题大都与程序化测试相反:相比后者的高度灵活性而言,前者的限制太多,只能执行工具作者认为用户需要的那些功能。


他们总是尝试从用户那里抽象出“困难”的概念,因此在编写请求时,你可能会有一张带有许多“参数”的小桌子。这些是查询参数吗?还是表单参数?也许它们实际上是 JSON 主体的一部分?如果有人在测试你的 HTTP API,但无法分辨出它们之间的区别,那么你可能应该思考一下,他们要做的测试是不是那么可靠了。


这些工具通常都支持“脚本”来克服它们的某些局限,但如果你用了太多脚本,结果可能会同时陷进两个坑里:抽象一大堆,同时难以理解的代码也数不清,这些代码指不定会做什么事情。


基于 GUI 的工具还有一个缺陷,那就是它们很难与 CI 管道集成,并且在大多数情况下,一旦你想以这种方式使用它们就得掏钱了(或者你只需要一两项高级功能也得升级到专业版)。


鉴于此,我认为它们非常适合探索性测试,但不适用于在 CI 中运行的自动化测试(或作为开发人员常用测试套件的一部分)。

替代方案:HTTP 文件

本质上,HTTP 文件是一个文件,你可以在其中手工编写 HTTP 请求,使用一些小的代码段对运行请求时收到的响应做出断言,并在同一文件中设置可由后续请求使用的变量。


有两种非常相似但并不相同的 HTTP 文件格式:



它们都允许开发人员编写 HTTP 请求,并加入诸如自动完成、输入时高亮显示错误,以及在已知内容类型时使用颜色高亮显示 HTTP 响应正文之类的细节。


你可以把它看作是在你喜欢的 IDE 中编写 HTTP“代码”。



顺便说一句:HTTP最初被设计为用作分布式对象系统的一个接口。它易于理解,在不需要任何高级工具帮助的情况下也能手工编写。


如果你以前没有看过 HTTP 文件,请参考 VS Code REST Client 文档中的一个小示例:


GET https://example.com/comments/1 HTTP/1.1
###
GET https://example.com/topics/1 HTTP/1.1
###
POST https://example.com/comments HTTP/1.1content-type: application/json
{ "name": "sample", "time": "Wed, 21 Oct 2015 18:27:50 GMT"}
复制代码


这非常简单。只要你稍微了解一点 HTTP(如果你正在测试 HTTP API,那你肯定没问题),就会知道这是在做什么。最大的好处是:这些代码可以在 VS Code Editor 和 IntelliJ IDEA Ultimate(以及终端或 CI,后文详解)中运行,因为它们的格式非常相似!


再对比一下基于 GUI 的工具,在 GUI 工具中,你对 HTTP 的了解几乎毫无用处,因为你必须学习另一种事物才行:



不幸的是,Jetbrains 尚未在社区(免费)版本中提供 HTTP 文件支持,但希望在 VS Code 的竞争推动下,他们会在未来某个时候补上这个功能(我不知道他们是否有这个计划,只是希望他们能做到)!


VS Code REST Client 格式比 Jetbrains 格式的功能更多一些,但截至本文撰写时,只有 Jetbrains 格式支持测试。Jetbrains 格式的另一项优势是,它有一个名为HTTP Request in Editor的规范,我认为这是一个很棒的起点。将来这一规范可以用作通用 HTTP 测试格式的基础,真是激动人心。


因此我将重点介绍 Jetbrains HTTP 文件格式,用来说明我们该如何将 HTTP 文件用于测试。

使用 Request-in-Editor 格式测试 HTTP API

我想明确一点,我与 Jetbrains 没有任何利益关系,推广它的产品也不会获得任何商业利益。我与 Jetbrains 的唯一关系是我的雇主为所有开发人员(包括我自己)支付 IDEA 许可证的费用,因此我可能被视为“间接客户”。


我认为 Request-in-Editor 格式(它的确应该改个顺口的名字)确实是一种非常适合测试 HTTP API 的东西。


首先,它使用了HTTP RFC本身描述的基本 HTTP 消息格式,但额外添加了一些内容,使其更易于手工编写。


例如,一个简单的 HTTP 请求能用一个简单的 URL 表示:


http://date.jsontest.com/
复制代码


运行此文件会对该 URL 发出单个 GET 请求。你在这里需要知道的是:


  • 如果省略方法指定,则 GET 是默认方法。

  • 如果省略协议版本,则 HTTP/1.1 是默认协议版本。

  • 从 URL 推断出必需的 Host 标头。


因此,通过网络发送的完整 HTTP 请求如下所示:


GET / HTTP/1.1Host: date.jsontest.com
复制代码


请注意,URL scheme http 不属于请求本身,因为它仅建立传输协议;并且由于 HTTP 的历史原因,CRLF 被用作换行符。


我不知道,为什么程序化 HTTP 客户端和 GUI 测试工具对测试人员太难处理了,并试图将其抽象化。对我来说,“真实的东西”看起来更顺眼一些。


作为测试的示例,让我们在 HTTP 文件中实现前文的 Java 示例中所示的测试:


GET https://api.github.com/users/eugenpAccept: application/vnd.github.v3+jsonUser-Agent: renatoathaydes
> {%client.test('Given User Exists, ' + 'When User Information Is Retrieved, ' + 'Then Retrieved Resource Is Correct', function() { client.assert(response.body.login === 'eugenp'); });%}
复制代码


就该这么简单。


请注意,原始的 Java 示例实际上并未设置AcceptUser-Agent标头。上面的示例这样做是因为 GitHub 建议对所有请求都这样做(并且User-Agent现在是必需的)。


这个示例中有一些值得注意的细节:


  • 在 HTTP 请求后立即在<{%%}标记之间编写用于测试响应的代码。

  • 确切地说,代码是用 JavaScript,ECMAScript5.1 编写的。

  • 有一些内置函数可用于测试。

  • 由于响应的内容类型,request.body会自动解析为 JSON。


可在这些代码段中使用的 JavaScript API 都列在了在IntelliJ文档中,并且在 IntelliJ IDEA Ultimate 中编写代码时也可以内联显示(对于 Jetbrains 官方的呼吁:请在社区版中提供此功能!!这不是什么“企业级”功能!),并带有自动补全。


这种格式的另一项重要特性是,你能使用变量并在响应句柄脚本和环境文件中指定它们,这些文件是用 JSON 编写的。


例如,你可以创建环境文件,这些文件必须称为http-client.env.jsonhttp-client.private.env.json(以保留机密数据),如下所示:


{    "development": {        "host": "localhost",        "id-value": 12345,        "username": "joe",        "password": "123",        "my-var": "my-dev-value"    },
"production": { "host": "example.com", "id-value": 6789, "username": "bob", "password": "345", "my-var": "my-prod-value" }}
复制代码


现在,你可以在 HTTP 文件中使用变量了:


GET http://{{host}}/api/json/get?id={{id-value}}Authorization: Basic {{username}} {{password}}Content-Type: application/json
{"key": {{my-var}}}
复制代码


你还可以使用 JavaScript 设置变量:


client.globals.set('my-var', 100);
复制代码

运行 Request-in-Editor 文件

显然,以 Request-in-Editor 格式运行 HTTP 文件的最简单方法是在 IntelliJ IDEA Ultimate 中运行。


运行时,它看起来就像是在 IDE 中运行单元测试一样:



不幸的是,Jetbrains 似乎没有制作一个独立的可执行文件来运行此类文件……但是我认为这种格式确实很酷,应该有一个可以让任何人以最小开销运行的 runner 实现,帮助你编写更好的 HTTP。即便你没有许可证,API 也会在命令行或 CI 中测试并执行它们。


因此,我编写了一个 HTTP 文件 runner 作为一个 Java 库,这是我自己的RawHTTP 项目(称为rawhttp-req-in-edit的一部分。


我还向RawHTTP CLI添加了 run 命令,这样你就可以执行能在 IntelliJ 中运行的任何 HTTP 文件。


现在要运行 HTTP 文件时,只需从终端运行以下命令:


rawhttp run my.http
复制代码


如果要指定环境,请使用-e 选项传递其名称:


rawhttp run my.http -e development
复制代码


这些代码都是开源的,并在 Apache2.0 许可下发布!


我希望这有助于 HTTP 文件的普及,并希望这种方法能在业内流行开来,因为俗话说:我们应该使用合适的工具来完成工作!在我看来,HTTP 文件应该是完成这一重要任务的最佳工具。

HTTP 文件的缺点

尽管 HTTP 文件很棒,但我认为仍然需要解决一些问题才能让它们变得更好。

JavaScript 响应处理程序

对于这种小型脚本来说,JS 并不是一个糟糕的选项,但如果能有其他方法就更好了。


考虑到 Java 支持运行多种语言,这一缺陷应该很容易解决。


如果有兴趣,我可能会在自己的 runner 实现中添加对其他语言的支持。如果你有兴趣,请创建一个GitHub问题


另一方面,Jetbrains 之所以为此使用了过时的 ECMA 版本,似乎只是因为他们受到了限制。我的猜测是,他们正在使用已过时的 Nashorn 引擎来运行 JS 代码。在更现代的引擎中(例如 GraalVM 的 Polyglot 框架),使用现代 JavaScript 和其他语言应该是非常容易的。

缺乏声明性断言

就像用 HTTP 编写 HTTP 比用其他任何一种编程语言编写 HTTP 都更顺手一样,用 HTTP 特定的断言语言编写声明性断言可能也比用 JS 代码来写更合适一些。


例如,我一直希望能够编写如下内容:


GET /resources/id-1Host: mywebsite.com
### Expected ResponseHTTP/1.1 200 .*Content-Type: application/jsonContent-Length: > 20
{{ body.id == 'id-1' ... }}
复制代码


如果能编写这样的测试就太好了!我们只需要一种易于编写和理解的,在大多数情况下足够灵活,特定于 HTTP 的模式语言……就算它有什么缺陷,我们仍然可以回退到脚本上。


不过,我实在没时间和精力做这种事情了,如果有人有意愿的话请随时与我联系!我可能会提出一些想法并提供一些帮助!

实现中的错误

IntelliJ 对 HTTP 文件的支持似乎还是 Beta 阶段一样……其中有一些明显的错误,当你开始编写更高级的文件时可能就会遇到它们。


希望随着这种格式的流行,官方会付出更多的努力来更好地支持它。

结论

我们测试 HTTP API 的方式应该比现在常用的方法更简单一些,我希望这篇文章至少能让一些人相信 HTTP 文件是一个好主意。


我希望本文能引起大家的兴趣,推动业界对此类文件制定标准,并在将来让 VS Code、IntelliJ IDEA 和其他工具都汇聚为一种格式,因为这将让所有人受益。


最后,虽然现在的情况还不够理想,但你也可以同时使用 VS Code 和 IntelliJ 编写 HTTP 文件,并且如果需要,可以使用 RawHTTP 的 Java 库或其 CLI 在 CI 中运行 HTTP 文件。


英文原文:


Writing HTTP files to test HTTP APIs


2020-06-09 15:483483
用户头像
王强 技术是文明进步的力量

发布了 785 篇内容, 共 374.1 次阅读, 收获喜欢 1709 次。

关注

评论

发布
暂无评论
发现更多内容

高并发中的atomic

Steven

2023-01-10:智能机器人要坐专用电梯把货物送到指定地点, 整栋楼只有一部电梯,并且由于容量限制智能机器人只能放下一件货物, 给定K个货物,每个货物都有所在楼层(from)和目的楼层(to),

福大大架构师每日一题

算法 rust Solidity 福大大

SeekBar(拖动条)

攻城狮Wayne

android SeekBar 拖动条

TiDB 底层存储结构 LSM 树原理介绍

京东科技开发者

技术 算法 LSM树 TiDB 企业号 1 月 PK 榜

MASA Stack 1.0发布会正式官宣

MASA技术团队

MASA MASA Stack

阿里P9耗时28天,总结历年亿级活动高并发系统设计手册

架构师之道

Java 高并发 架构师

架构实战营4.1 数据库存储架构随堂练习

西山薄凉

「架构实战营」

如何用「标准差」度量研发波动

feijieppm

项目管理 研发效能 技术管理 文化 & 方法 效能度量

Dubbo-kubernetes 基于 Informer 服务发现优化之路

阿里巴巴中间件

阿里云 Kubernetes 云原生 dubbo

DevSecOps 与软件开发安全

SoFlu软件机器人

龙湖千丁基于 ACK@Edge 的云原生智慧停车系统架构实践

阿里巴巴中间件

阿里云 云原生

为什么开发者这么看重SQL?看完这些应用场景你就明白了

雨果

sql 数据库管理工具 SQL开发工具

先行试点,创新改造:中信期货关键业务系统自主可控的实践之路

OceanBase 数据库

JDK结构介绍

Steven

DiT:Transformers 与扩散模型强强联手

Zilliz

AI 算法模型

《PyTorch 深度学习实战》学习笔记 --NumPy(上)

IT蜗壳-Tango

H5直播技术起航

京东科技开发者

音视频 编码 H5 flv 企业号 1 月 PK 榜

mouseover 和 mouseenter 的区别

ModStart

走进AI图像生成核心技术 - Diffusion

Baihai IDP

人工智能 AI AIGC Diffusion 扩散模型

APISIX+Dubbo+Nacos 最佳实践

阿里巴巴中间件

阿里云 云原生 dubbo nacos APISIX

钉钉 IM 基于 RocketMQ 5.0 的云原生应用实践

阿里巴巴中间件

阿里云 RocketMQ 云原生

阿里云计算巢 x GBase GCDW:自动化部署云原生数据仓库

云布道师

阿里云

面试题:为什么不建议在MySQL中使用UTF-8?

风铃架构日知录

Java MySQL 程序员 后端 IT

LogicFlow自定义业务节点

小鑫同学

前端 vite Vue 3

马斯克收购推特后,亲自与员工探讨了……

博文视点Broadview

Flink 批作业的运行时自适应执行管控

Apache Flink

大数据 flink 实时计算

神经网络基础部件-损失函数详解

嵌入式视觉

激活函数 Relu sigmoid tanh swish激活函数

Java高手速成 | 新增类Record的工作实例

TiAmo

新特性 Java’

PHP 中命令行调用 escapeshellarg 函数中文问题

ModStart

ClassIn:如何打造更稳定的Zabbix监控系统

OceanBase 数据库

oceanbase 数据库·

KaiwuDB 1.0 - 时序数据库系列产品正式发布

KaiwuDB

数据库

手把手教你编写HTTP文件测试HTTP API_文化 & 方法_Renato Athaydes_InfoQ精选文章