1. 概述
在本教程中,我們將重點介紹通過集成測試測試 REST API 的原理和機制。 具體來説,我們將使用實時請求和 JSON 負載來測試 API,以確保其正確性和行為。
2. API 集成測試
API 集成測試涉及測試我們的應用程序與其外部依賴項(如數據庫、第三方服務或其它 API)之間的交互。 目標是確保系統中的不同組件協同工作,按預期運行。 與專注於單個組件的單元測試不同,集成測試通過向我們的 API 發送實際的 HTTP 請求並檢查響應,來模擬真實世界的使用情況,從而確保一切運行正常。
2.1. API 集成測試的重要性
雖然單元測試確保單個代碼片段按預期工作,但它們無法捕獲由於這些組件集成而產生的任何問題。 集成測試通過檢查不同系統組件之間的通信,來解決這些問題,確保組件之間的誤解或錯誤不會擾亂整體功能。
此外,它們還能讓團隊對系統在生產環境中按預期工作建立信心,從而降低部署後出現故障的風險。
2.2. 進行 API 集成測試的時間
通常,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));
}
這是一個相當簡單的測試。 它驗證基本快樂路徑是否正常工作,而沒有為測試套件添加過多的複雜性。
如果,無論出於何原因,它失敗了,我們就無需查看此URL上的任何其他測試,直到我們修復它。
4. 測試媒體類型
我們可以也測試媒體類型,以確保響應的默認 Content-Type
@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 負載。
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,但通常,響應的 Content-Type 標頭應與請求的 Accept 標頭一起測試。客户端通過 Accept 標頭請求特定類型的表示形式,服務器應尊重它。
6. 測試工具
我們將使用 Jackson 2 將原始 JSON 字符串反序列化為類型安全的 Java Entity:
public class GitHubUser {
private String login;
// 標準的 getter 和 setter
}
我們僅使用一個簡單的實用工具來保持測試的清潔、可讀性和高層次抽象:
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 用户資源的 Representation 相當複雜,我們此處不需要任何這些信息。
7. 依賴項
這些工具和測試使用以下庫,所有庫均可在 Maven Central 上找到:
- HttpClient
- Jackson 2
- Hamcrest (可選)
8. 結論
這只是完整的集成測試套件的一部分。這些測試側重於確保 REST API 的基本正確性,而沒有深入到更復雜的場景。
例如,我們沒有涵蓋以下內容:API 的可發現性、相同資源的各種表示形式的消費等。