Автор оригинала: Eugen Paraschiv.
1. Обзор
Этот учебник посвящен основным принципам и механике тестирования REST API с помощью live Integration Tests (с полезной нагрузкой 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( "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 staticT 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, поэтому его должно быть легко импортировать и запускать как есть.