Статьи

Тестирование интеграции Java RESTful API

Этот пост будет посвящен основным принципам и механизмам написания интеграционных тестов Java для RESTful API (с полезной нагрузкой JSON).

Цель состоит в том, чтобы дать представление о технологиях и написать несколько тестов на базовую правильность. В примерах используется последняя версия REST API GitHub.

Для внутреннего приложения этот вид тестирования обычно выполняется в качестве позднего шага в процессе непрерывной интеграции, потребляя REST API после того, как он уже развернут.

При тестировании ресурса REST обычно есть несколько ортогональных обязанностей, на которых должны сосредоточиться тесты:

  • код ответа HTTP
  • другие заголовки HTTP в ответе
  • полезная нагрузка (JSON, XML)

Каждый тест должен быть сосредоточен только на одной ответственности и включать одно утверждение. Сосредоточение внимания на четком разделении всегда имеет свои преимущества, но при проведении такого рода тестирования «черного ящика» это еще более важно, так как общая тенденция заключается в написании сложных сценариев тестирования в самом начале.

Другим важным аспектом интеграционных тестов является соблюдение принципа единого уровня абстракции — логика в тесте должна быть написана на высоком уровне. Такие детали, как создание запроса, отправка HTTP-запроса на сервер, работа с IO и т. Д., Должны выполняться не встроенно, а с помощью служебных методов.

Тестирование кода ответа HTTP

01
02
03
04
05
06
07
08
09
10
11
12
13
@Test
public void givenUserDoesNotExists_whenUserInfoIsRetrieved_then404IsReceived()
      throws ClientProtocolException, IOException{
   // Given
   String name = randomAlphabetic( 8 );
   HttpUriRequest request = new HttpGet( "https://api.github.com/users/" + name );
    
   // When
   HttpResponse httpResponse = httpClient.execute( request );
    
   // Then
   RestAssert.assertResponseCodeIs( httpResponse, 404 );
}

Это довольно простой тест, который проверяет, работает ли базовый «счастливый путь», без особой сложности для набора тестов. Если по какой-либо причине он не работает, то нет необходимости искать какой-либо другой тест для этого URL, пока он не будет исправлен. Поскольку проверка кода ответа является одним из наиболее распространенных утверждений комплекта тестирования интеграции, используется настраиваемое утверждение .

1
2
3
4
5
public static void assertResponseCodeIs
      ( final HttpResponse response, final int expectedCode ){
   final int statusCode = httpResponse.getStatusLine().getStatusCode();
   assertEquals( expectedCode, statusCode );
}

Тестирование других заголовков ответа HTTP

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@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 = this.httpClient.execute( request );
    
   // Then
   String mimeType = EntityUtils.getContentMimeType( response.getEntity() );
   assertEquals( jsonMimeType, mimeType );
}

Это гарантирует, что ответ при запросе данных пользователя на самом деле является JSON. Существует логическая последовательность тестируемой функциональности — сначала код ответа, чтобы убедиться, что запрос был в порядке, затем тип MIME-запроса и только потом проверка, что фактический JSON верен.

Тестирование полезной нагрузки JSON HTTP-ответа

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Test
public void givenUserExists_whenUserInformationIsRetrieved_thenRetrievedResourceIsCorrect()
      throws ClientProtocolException, IOException{
   // Given
   HttpUriRequest request = new HttpGet( "https://api.github.com/users/eugenp" );
    
   // When
   HttpResponse response = new DefaultHttpClient().execute( request );
    
   // Then
   GitHubUser resource =
      RetrieveUtil.retrieveResourceFromResponse( response, GitHubUser.class );
   assertThat( "eugenp", Matchers.is( resource.getLogin() ) );
}

В этом случае я знаю, что представлением ресурсов GitHub по умолчанию является JSON, но обычно заголовок Content-Type ответа должен проверяться вместе с заголовком Accept запроса — клиент запрашивает определенный тип представления через Accept, который Сервер должен соблюдать.

Утилиты для тестирования

Вот утилиты, которые позволяют тестам оставаться на более высоком уровне абстракции:

— украсить HTTP-запрос полезной нагрузкой JSON (или непосредственно POJO):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public static < T >HttpEntityEnclosingRequest decorateRequestWithResource
      ( final HttpEntityEnclosingRequest request, final T resource )
      throws IOException{
   Preconditions.checkNotNull( request );
   Preconditions.checkNotNull( resource );
    
   final String resourceAsJson = JsonUtil.convertResourceToJson( resource );
   return JsonUtil.decorateRequestWithJson( request, resourceAsJson );
}
 
public static HttpEntityEnclosingRequest decorateRequestWithJson
      ( final HttpEntityEnclosingRequest request, final String json )
      throws UnsupportedEncodingException{
   Preconditions.checkNotNull( request );
   Preconditions.checkNotNull( json );
    
   request.setHeader( HttpConstants.CONTENT_TYPE_HEADER, "application/json" );
   request.setEntity( new StringEntity( json ) );
    
   return request;
}

— получить полезную нагрузку JSON (или непосредственно POJO) из ответа HTTP:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public static String retrieveJsonFromResponse( final HttpResponse response )
      throws IOException{
   Preconditions.checkNotNull( response );
    
   return IOUtils.toString( response.getEntity().getContent() );
}
 
public static < T >T retrieveResourceFromResponse
      ( final HttpResponse response, final Class< T > clazz ) throws IOException{
   Preconditions.checkNotNull( response );
   Preconditions.checkNotNull( clazz );
    
   final String jsonFromResponse = retrieveJsonFromResponse( response );
   return ConvertUtil.convertJsonToResource( jsonFromResponse, clazz );
}

— утилиты преобразования в и из объекта Java (POJO) в JSON:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public static < T >String convertResourceToJson( final T resource )
      throws IOException{
   Preconditions.checkNotNull( resource );
   
  return new ObjectMapper().writeValueAsString( resource );
}
 
public static < T >T convertJsonToResource
      ( final String json, final Class< T > clazzOfResource ) throws IOException{
  Preconditions.checkNotNull( json );
  Preconditions.checkNotNull( clazzOfResource );
   
  return new ObjectMapper().readValue( json, clazzOfResource );
}

зависимости

Утилиты и тесты используют следующие библиотеки, все доступные в Maven central:

Вывод

Это только одна часть того, каким должен быть полный комплект тестирования интеграции. Тесты направлены на обеспечение базовой правильности API REST, не вдаваясь в более сложные сценарии, обнаруживаемость API, использование различных представлений для одного и того же ресурса или других более сложных областей. Я расскажу об этом в следующем посте, а пока ознакомьтесь с полным проектом на github .

Ссылка: Введение в интеграционное тестирование Java для RESTful API от нашего партнера JCG Юджина Параскива в блоге baeldung .

Статьи по Теме :