用Java测试REST API
1. 概述
本教程重点介绍使用实时集成测试(使用 JSON 有效负载)测试 REST API 的基本原则和机制。
主要目标是介绍测试 API 的基本正确性——我们将使用最新版本的GitHub REST API 来提供示例。
对于内部应用程序,这种测试通常会作为持续集成过程的最后一步运行,在部署后使用 REST API。
在测试 REST 资源时,测试通常应该关注一些正交职责:
- HTTP响应代码
- 响应中的其他 HTTP标头
- 有效负载(JSON 、XML)
**每个测试都应该只关注一个职责并包含一个断言。**专注于清晰的分离总是有好处的,但是在进行这种黑盒测试时更为重要,因为一般趋势是在一开始就编写复杂的测试场景。
集成测试的另一个重要方面是遵守单层抽象原则——测试中的逻辑应该在高层次上编写。诸如创建请求、向服务器发送 HTTP 请求、处理 IO 等细节不应内联完成,而应通过实用程序方法完成。
2. 测试状态码
@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));
}
这是一个相当简单的测试——它验证基本的快乐路径是否有效,而不会给测试套件增加太多复杂性。
如果出于某种原因,它失败了,那么在解决此问题之前,无需查看此 URL 的任何其他测试。
3. 测试媒体类型
@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 负载
@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 实体:
public class GitHubUser {
private String login;
// standard getters and setters
}
我们只使用一个简单的实用程序来保持测试的干净、可读性和高度抽象:
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 中心可用:
- HttpClient
- Jackson 2
- Hamcrest (可选)