作者: Eugen Paraschiv
译者: helloworldtang
目录
- 1. 概览
- 2. 测试状态码
- 3. 测试媒体类型
- 4. 测试接口返回的JSON
- 5. 测试利器
- 6. 依赖
- 7. 总结
1. 概览
本教程重点介绍使用自动化IT(集成测试)测试REST API的基本原则和机制。
我们的主要目标是介绍如何测试API的可用性——示例将使用最新版本的 GitHub REST API。 对于内部应用程序,此类测试通常在部署REST API之后,作为持续集成的后期步骤运行。
在测试REST资源时,通常会有一些正交的职责需要关注:
- HTTP响应代码
- 响应中的其他HTTP头
- 有效负载(JSON,XML)
每个测试用例应该只关注单个职责,并包含一个断言。清晰的关注点分离总是有好处的,并且在这种黑盒测试中就更重要了,因为通常的情况是在一开始就编写复杂的测试用例。
集成测试的另一个重要原则是坚持单一抽象层级——业务逻辑应该在更高层级的用例中完成。诸如创建请求、向服务器发送HTTP请求、处理IO等细节应该委托给第三方库,而不是自己实现并且到处散落在测试用例中。
2. 测试状态码
代码语言:javascript复制@Test
public void givenUserDoesNotExists_whenUserInfoIsRetrieved_then404IsReceived()
throws ClientProtocolException, IOException {
// Given
String name = RandomStringUtils.randomAlphabetic( 8 );
HttpUriRequest request = new HttpGet( "https://api.github.com/users/" name );
// When
HttpResponse httpResponse = HttpClientBuilder.create().build().execute( request );
// Then
assertThat(
httpResponse.getStatusLine().getStatusCode(),
equalTo(HttpStatus.SC_NOT_FOUND));
}
这是一个相当简单的测试——它用来检查一个API是否是可用的,并不会给测试用例增加太多的复杂性。
不管出于什么原因它失败了,那么在被修复之前,我们就不需要查看这个API相关的测试用例。
3. 测试媒体类型
代码语言:javascript复制@Test
public void
givenRequestWithNoAcceptHeader_whenRequestIsExecuted_thenDefaultResponseContentTypeIsJson()
throws ClientProtocolException, IOException {
// Given
String jsonMimeType = "application/json";
HttpUriRequest request = new HttpGet( "https://api.github.com/users/eugenp" );
// When
HttpResponse response = HttpClientBuilder.create().build().execute( request );
// Then
String mimeType = ContentType.getOrDefault(response.getEntity()).getMimeType();
assertEquals( jsonMimeType, mimeType );
}
这个测试用例确保了服务器响应的资源表述是JSON。
正如您可能已经注意到的,我们在按照一个循序渐进的方式进行测试 ——首先是响应状态码(确保接口是可用的),然后是服务器响应的媒体类型,并且只有到下一个测试用例,我们才会检查接口返回的JSON数据。
4. 测试接口返回的JSON
代码语言:javascript复制@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() ) );
}
在上面的测试用例中,我们知道GitHub资源的默认表述是JSON,但在通常情况下,响应的Content-Type头应该与请求的Accept头一起测试——客户端通过Accept请求资源特定类型的表述,这是服务器应该遵守的。
5. 测试利器
我们将使用Jackson 2将JSON字符串反序列化成一个类型安全的Java实体:
代码语言:javascript复制public class GitHubUser {
private String login;
// standard getters and setters
}
我们只使用一个简单的工具来保持测试用例的整洁、可读性和一个比较高的抽象层级:
代码语言:javascript复制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);
}
注意,Jackson忽略了GitHub API发送给我们的未知属性 ——这仅仅是因为GitHub上用户资源的表述变得非常复杂——我们在这里不需要其它的信息。
6. 依赖
测试工具和测试用例使用的库在Maven Central上都是可用的,如下所示:
- HttpClient
- Jackson 2
- Hamcrest (可选的)
7. 总结
上面的示例只是完整集成测试的一部分。测试着重于确保REST API的正确性,而不必涉及更复杂的情况,譬如,以下内容都没有涉及:API的可发现性、对同一资源使用不同的表述等等。
所有这些示例和代码片段都可以在Github上找到——这是一个基于maven的项目,因此应该很容易导入和运行。