Contents

用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 中心可用: