本文最初发布于Renato Athaydes的个人网站,经原作者授权由InfoQ中文站翻译并分享。
目前,为HTTP API编写自动化测试实在有点太复杂了。一般来说,你可以通过两种方法来解决这个问题:
我认为这些方法都不够理想。进一步来说,我认为这里有另一种解决方案,它在大多数情况下都好用得多,但知道的人却很少:那就是直接在一个文本文件中编写HTTP,同时添加一些人性化的便利内容,带来更愉快的测试体验。
在这篇文章中,我会阐述当前方法中存在哪些问题,以及为什么HTTP文件是一个很好的替代方案。
用你喜欢的编程语言编写测试时,你有无限的灵活性。基本上,你想到什么就能做什么,因此就灵活性而言,程序化测试绝对是最佳选择。
但这同时也是程序化测试的问题所在:由于它们太通用了,因此代码很难阅读和维护。如果测试足够复杂,甚至连程序要测试的是什么东西都很难搞清楚。
这是很常见的状况,而且往往会带来一个问题:测试程序本身由谁来测试呢?你能保证测试失败是由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的测试工具所面临的问题大都与程序化测试相反:相比后者的高度灵活性而言,前者的限制太多,只能执行工具作者认为用户需要的那些功能。
他们总是尝试从用户那里抽象出“困难”的概念,因此在编写请求时,你可能会有一张带有许多“参数”的小桌子。这些是查询参数吗?还是表单参数?也许它们实际上是JSON主体的一部分?如果有人在测试你的HTTP API,但无法分辨出它们之间的区别,那么你可能应该思考一下,他们要做的测试是不是那么可靠了。
这些工具通常都支持“脚本”来克服它们的某些局限,但如果你用了太多脚本,结果可能会同时陷进两个坑里:抽象一大堆,同时难以理解的代码也数不清,这些代码指不定会做什么事情。
基于GUI的工具还有一个缺陷,那就是它们很难与CI管道集成,并且在大多数情况下,一旦你想以这种方式使用它们就得掏钱了(或者你只需要一两项高级功能也得升级到专业版)。
鉴于此,我认为它们非常适合探索性测试,但不适用于在CI中运行的自动化测试(或作为开发人员常用测试套件的一部分)。
本质上,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.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的了解几乎毫无用处,因为你必须学习另一种事物才行:
不幸的是,Jetbrains尚未在社区(免费)版本中提供HTTP文件支持,但希望在VS Code的竞争推动下,他们会在未来某个时候补上这个功能(我不知道他们是否有这个计划,只是希望他们能做到)!
VS Code REST Client格式比Jetbrains格式的功能更多一些,但截至本文撰写时,只有Jetbrains格式支持测试。Jetbrains格式的另一项优势是,它有一个名为HTTP Request in Editor的规范,我认为这是一个很棒的起点。将来这一规范可以用作通用HTTP测试格式的基础,真是激动人心。
因此我将重点介绍Jetbrains HTTP文件格式,用来说明我们该如何将HTTP文件用于测试。
我想明确一点,我与Jetbrains没有任何利益关系,推广它的产品也不会获得任何商业利益。我与Jetbrains的唯一关系是我的雇主为所有开发人员(包括我自己)支付IDEA许可证的费用,因此我可能被视为“间接客户”。
我认为Request-in-Editor格式(它的确应该改个顺口的名字)确实是一种非常适合测试HTTP API的东西。
首先,它使用了HTTP RFC本身描述的基本HTTP消息格式,但额外添加了一些内容,使其更易于手工编写。
例如,一个简单的HTTP请求能用一个简单的URL表示:
http://date.jsontest.com/
运行此文件会对该URL发出单个GET请求。你在这里需要知道的是:
因此,通过网络发送的完整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示例实际上并未设置
Accept
和User-Agent
标头。上面的示例这样做是因为GitHub建议对所有请求都这样做(并且User-Agent
现在是必需的)。
这个示例中有一些值得注意的细节:
<{%
和%}
标记之间编写用于测试响应的代码。request.body
会自动解析为JSON。可在这些代码段中使用的JavaScript API都列在了在IntelliJ文档中,并且在IntelliJ IDEA Ultimate中编写代码时也可以内联显示(对于Jetbrains官方的呼吁:请在社区版中提供此功能!!这不是什么“企业级”功能!),并带有自动补全。
这种格式的另一项重要特性是,你能使用变量并在响应句柄脚本和环境文件中指定它们,这些文件是用JSON编写的。
例如,你可以创建环境文件,这些文件必须称为http-client.env.json
或http-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格式运行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文件很棒,但我认为仍然需要解决一些问题才能让它们变得更好。
对于这种小型脚本来说,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文件。
英文原文:
领取专属 10元无门槛券
私享最新 技术干货