写点什么

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

发布了 836 篇内容, 共 444.1 次阅读, 收获喜欢 1753 次。

关注

评论

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

回“疫”录(17):返宁的前一天

小天同学

疫情 回忆录 现实纪录 纪实

零基础如何学架构

兆熊

架构

阿里巴巴泰山版《Java 开发者手册》,也是一份防坑指南

古时的风筝

Java规范 Java开发手册

JVM源码分析之Object.wait/notify实现

猿灯塔

JVM

数列找规律的问题

oldj

数学

网站系统架构演进

Janenesome

读书笔记 程序员 架构 系统设计

决战下半场:小程序技术助力金融APP重回C位

FinClip

小程序 数字化转型 app重构

初次见面

KAMI

Web3极客日报#140

谢锐 | Frozen

区块链 独立开发者 技术社区 Rebase Web3 Daily

看完这篇HTTP,跟面试官扯皮就没问题了

苹果看辽宁体育

https okhttp

如何在 Mac 上优雅的截图和录屏

Winann

macos 效率 效率工具 Mac

谈谈控制感(4):损失的后果很严重

史方远

职场 心理 成长

Java实现Base64

Java

为什么软件开发很难外包

刘华Kenneth

外包 DevOps 风险 背锅

高仿瑞幸小程序 09 云数据库初体验

曾伟@喵先森

小程序 微信小程序 大前端 移动

游戏夜读 | Scikit-learn的2018自述

game1night

奔腾吧,“后浪”李子柒!

无量靠谱

网红

经历过疫情,你懂得了什么

Winann

疫情 个人成长 生活 成长

程序员的晚餐 | 5 月 15 日 如果不写代码了,那就开个饺子店

清远

美食

服务发现:ZooKeeper vs etcd vs Consul

Tux Hu

Docker 容器 微服务 etcd Consul

浅析 - CocoaLumberjack 3.6 之 DatabaseLogger

Edmond

ios sqlite log4j CocoaLumberjack DDLog

记一次MHA切换故障踩的坑

一个有志气的DB

MySQL 高可用 复制 主从同步 故障分析

战略懒惰有多可怕

Neco.W

创业 重新理解创业 初创公司

Leetcode 556. Next Greater Element III

隔壁小王

算法

一篇文章搞定Java处理Excel的各种疑难杂症

知春秋

Java Excel POI

游戏发行中学到的重要经验(严肃长文)

谢锐 | Frozen

独立开发者 游戏开发 游戏出海 移动互联网

“四个维度” 讲明白什么是微服务!

攀岩飞鱼

微服务 单体系统 架构设计 团队组织 康威定律

谈谈双亲委派模型的第四次破坏-模块化

寻筝

Java JVM

找一个更好的理由

史方远

职场 成长 工作

elasticsearch源码解析(一)——restapi

罗琦

elasticsearch 源码分析 RESTful

Azure App 部署Django 和 PostgrSQL

yann [扬] :曹同学

Python azure

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