NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

手把手教你编写 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:483549
用户头像
王强 技术是文明进步的力量

发布了 788 篇内容, 共 378.5 次阅读, 收获喜欢 1719 次。

关注

评论

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

记录一次数据库CPU被打满的排查过程

京东科技开发者

数据库 cpu cpu飙满 调优 慢SQL

Spring Security系列教程17--注销登录的实现及原理分析

一一哥

spring security spring-boot 注销登录

Tapdata 获阿里云首批产品生态集成认证,携手阿里云共建新合作

tapdata

阿里云 Tapdata

如何通过经纬度坐标获取附近的地址信息?

HMS Core

定位

BAT大厂都在用的Docker。学会这三招,面试、工作轻松hold住

霍格沃兹测试开发学社

mysql基础

楠羽

#开源

Selenium 中的 JUnit 注解

FunTester

Rancher 2.6 全新 Logging 快速入门(2)

Rancher

Kubernetes k8s rancher

Git 实战(三) | Github 必会高频基础命令与 IDE 的 Git 集成

霍格沃兹测试开发学社

App自动化之dom结构和元素定位方式(包含滑动列表定位)

霍格沃兹测试开发学社

云原生(三十一) | Kubernetes篇之平台基本预装资源

Lansonli

云原生 k8s 8月月更

Git实战(四)| Git分支管理实操,搞定在线合并和本地合并

霍格沃兹测试开发学社

持久,redis 持久化有哪几种方式,怎么选?

知识浅谈

redis持久化 8月月更

源码解析 kubectl port-forward 工作原理

张晓辉

Kubernetes 云原生 源码解析

佛山复星禅诚医院黄汉森:云边协同,打造线上线下一体化智慧医疗

阿里云弹性计算

弹性计算 分布式云 云盒 异地双活

面试突击79:Bean 作用域是啥?它有几种类型?

王磊

Java 常见面试题

MySQL查询重写插件

TimeFriends

8月月更

直播预告丨阿里云佐井:关注预警6要素,帮助用户实现精准监控和告警

阿里云弹性计算

监控 预警

SUSE 加速汽车行业智能化发展

Rancher

Kubernetes k8s rancher

Tapdata 杨哲轩:如何在零售行业实施主数据治理?

tapdata

Tapdata

BAT 大厂最流行的性能压测、监控、剖析技术体系解析

霍格沃兹测试开发学社

Docker 镜像构建可以分享的快乐

霍格沃兹测试开发学社

混迹职场10多年的数据开发老鸟,居然被一个职场新人上了一课

雨果

数据工程师

超大规模跨域集群统一监控实践

移动云大数据

技术分享 | 黑盒测试方法论—场景法

霍格沃兹测试开发学社

Git实战(五)| 让工作更高效,搞定Git的分支管理

霍格沃兹测试开发学社

开源,无禁止即可为

Databend

开源社区 大数据 开源 #开源 databend

什么?MySQL的等值查询竟然出错了??

转转技术团队

MySQL

极简云上分析,释放数据价值|Kyligence 邀您参加2022秋季线上论坛

Kyligence

数据分析 数据价值 数据管理 智能多维数据库

30 分钟轻松搞定正则表达式基础

霍格沃兹测试开发学社

在window下使用 VScode 搭建 ARM 开发环境

矜辰所致

开发工具 开发环境 arm 8月月更

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