Тествайте REST API с Java

1. Общ преглед

Този урок се фокусира върху основните принципи и механика на тестване на REST API с тестове за интеграция на живо (с полезен товар JSON).

Основната цел е да се предостави въведение за тестване на основната коректност на 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( "//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( "//api.github.com/users/eugenp" ); // When HttpResponse response = HttpClientBuilder.create().build().execute( request ); // Then String mimeType = ContentType.getOrDefault(response.getEntity()).getMimeType(); assertEquals( jsonMimeType, mimeType ); }

Това гарантира, че Response действително съдържа JSON данни.

Както може би сте забелязали, ние следваме логическа прогресия на тестовете - първо кода на състоянието на отговора (за да се уверим, че заявката е в ред), след това медийният тип на отговора и едва в следващия тест ще разгледаме действителен полезен товар на JSON.

4. Тестване на полезния товар на JSON

@Test public void givenUserExists_whenUserInformationIsRetrieved_thenRetrievedResourceIsCorrect() throws ClientProtocolException, IOException { // Given HttpUriRequest request = new HttpGet( "//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 String в типов Java обект:

public class GitHubUser { private String login; // standard getters and setters }

Използваме само проста помощна програма, за да поддържаме тестовете чисти, четливи и на високо ниво на абстракция:

public static  T retrieveResourceFromResponse(HttpResponse response, Class 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); }

Забележете, че Джаксън игнорира неизвестни свойства, които API на GitHub ни изпраща - това е просто защото представянето на потребителски ресурс в GitHub става доста сложно - и ние не се нуждаем от тази информация тук.

6. Зависимости

Помощните програми и тестовете използват следните библиотеки, всички налични в Maven central:

  • HttpClient
  • Джаксън 2
  • Hamcrest (по избор)

7. Заключение

Това е само една част от това, което трябва да бъде пълният пакет за тестване на интеграция. Тестовете се фокусират върху осигуряване на основна коректност за REST API , без да се навлиза в по-сложни сценарии,

Например не са обхванати следните: Откриваемост на API, консумация на различни представления за един и същи ресурс и т.н.

Внедряването на всички тези примери и кодови фрагменти може да бъде намерено в Github - това е проект, базиран на Maven, така че трябва да е лесно да се импортира и да се изпълнява както е.