1. 概述

本教程将聚焦于如何通过集成测试来验证 REST API 的行为与正确性。我们将发送真实的 HTTP 请求并解析 JSON 响应,从而确保 API 在实际使用场景下表现符合预期。

2. API 集成测试

API 集成测试关注的是我们的应用与外部依赖(如数据库、第三方服务或其他 API)之间的交互。✅ 它的目标是验证系统中各个组件是否能协同工作。

与单元测试不同,集成测试不是孤立地测试单个类或方法,而是模拟真实请求流程,检查 API 的输入输出是否符合预期。

2.1. 为什么需要 API 集成测试

虽然单元测试能保证代码模块本身没问题,但无法发现模块之间协作时的潜在问题。⚠️ 集成测试正好补上了这一环,它验证了模块间通信是否顺畅,避免出现“各自安好,合体翻车”的尴尬。

此外,它还能增强团队对上线系统的信心,减少部署后的故障风险。

2.2. 何时进行 API 集成测试

一般来说,集成测试是在单元测试之后、正式发布之前进行的。当单元测试确认各个模块工作正常后,集成测试就负责检查这些模块是否能“和睦相处”。

特别是在引入新功能或更新时,集成测试是防止回归问题的重要防线。

3. 测试状态码

为了验证 API 在用户不存在时的响应是否正确,我们可以测试返回的状态码:

@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 的基本行为已经出问题,其他测试也失去了意义。

4. 测试媒体类型

我们还可以验证响应的 Content-Type 是否为 JSON,以确保返回的是结构化数据:

@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 格式,这是后续解析 payload 的前提。逻辑上我们是逐步验证:先看状态码,再看媒体类型,最后才解析内容。

5. 测试 JSON 响应体

为了验证 API 是否返回了正确的数据,我们可以解析响应体中的 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,但在实际项目中,我们通常会同时检查请求头中的 Accept 和响应头中的 Content-Type,确保客户端和服务端在“说同一种语言”。

6. 测试辅助工具

我们使用 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 时它返回的字段较多,我们只关心必要的字段。

7. 依赖项

以下是我们测试中用到的库,均可在 Maven Central 找到:

8. 总结

本文只是完整集成测试体系中的一部分,主要关注 REST API 的基本功能验证。✅ 我们没有涉及更复杂的场景,例如:

❌ API 的可发现性
❌ 同一资源的多种表示方式(Content Negotiation)等

后续可以根据项目需求,逐步补充这些测试维度。


原始标题:Test a REST API with Java | Baeldung