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

发布于:2020 年 6 月 9 日 15:48

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

手把手教你编写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 API

顺便说一句: 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.1
content-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 的了解几乎毫无用处,因为你必须学习另一种事物才行:

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

不幸的是,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.1
Host: date.jsontest.com

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

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

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

复制代码
GET https://api.github.com/users/eugenp
Accept: application/vnd.github.v3+json
User-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 中运行单元测试一样:

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

不幸的是,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-1
Host: mywebsite.com
### Expected Response
HTTP/1.1 200 .*
Content-Type: application/json
Content-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

阅读数:3 发布于:2020 年 6 月 9 日 15:48

评论

发布
暂无评论