1. Обзор
В этом руководстве мы рассмотрим инфраструктуру тестирования Джерси и посмотрим, как мы можем использовать ее для быстрого написания интеграционных тестов.
Как мы уже видели в предыдущих статьях, Jersey — это платформа с открытым исходным кодом для разработки RESTful Web Services . Мы можем узнать больше о Джерси в нашей статье «Введение в создание API с помощью Джерси и Spring» — здесь .
2. Настройка приложения
Jersey Test Framework — это инструмент, помогающий нам проверить правильность реализации наших серверных компонентов. Как мы увидим позже, он обеспечивает быстрый и простой способ написания интеграционных тестов и очень хорошо справляется с взаимодействием с нашими HTTP API.
Точно так же он работает практически «из коробки» и его легко интегрировать с нашими проектами на базе Maven . Фреймворк в основном основан на JUnit, хотя его также можно использовать с TestNG, что делает его пригодным для использования практически во всех средах.
В следующем разделе мы увидим, какие зависимости нам нужно добавить в наше приложение, чтобы использовать фреймворк.
2.1. Зависимости Maven
Прежде всего, давайте добавим базовую зависимость Jersey Test Framework в наш pom.xml
:
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>2.27</version>
<scope>test</scope>
</dependency>
Как всегда, последнюю версию мы можем получить с Maven Central .
Почти все тесты на Джерси используют де-факто тестовую фабрику контейнеров Grizzly, которую нам также нужно будет добавить:
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.27</version>
<scope>test</scope>
</dependency>
Снова последнюю версию мы можем найти в Maven Central .
3. Начало работы
В следующем разделе мы рассмотрим основные шаги, необходимые для написания простого теста.
Начнем с тестирования простого ресурса Greetings
на нашем сервере:
@Path("/greetings")
public class Greetings {
@GET
@Path("/hi")
public String getHiGreeting() {
return "hi";
}
}
3.1. Настройка теста
Теперь давайте определим наш тестовый класс:
public class GreetingsResourceIntegrationTest extends JerseyTest {
@Override
protected Application configure() {
return new ResourceConfig(Greetings.class);
}
//...
}
В приведенном выше примере мы видим, что для разработки теста с использованием Jersey Test Framework наш тест должен быть подклассом JerseyTest
.
Затем мы переопределяем метод configure
, который возвращает пользовательскую конфигурацию ресурса для нашего теста и содержит только ресурс Greetings .
Это, конечно, тот ресурс, который мы хотим протестировать.
3.2. Пишем наш первый тест
Давайте начнем с тестирования простого запроса GET от нашего API приветствия:
@Test
public void givenGetHiGreeting_whenCorrectRequest_thenResponseIsOkAndContainsHi() {
Response response = target("/greetings/hi").request()
.get();
assertEquals("Http Response should be 200: ", Status.OK.getStatusCode(), response.getStatus());
assertEquals("Http Content-Type should be: ", MediaType.TEXT_HTML, response.getHeaderString(HttpHeaders.CONTENT_TYPE));
String content = response.readEntity(String.class);
assertEquals("Content of ressponse is: ", "hi", content);
}
Обратите внимание, что у нас есть полный доступ к ответу HTTP , поэтому мы можем выполнять такие действия, как проверка кода состояния, чтобы убедиться, что операция действительно прошла успешно, или работать с фактическим телом ответа .
Поясним подробнее, что мы делаем в приведенном выше примере:
- Отправьте запрос HTTP GET на «/greetings/hi»
- Проверьте код состояния HTTP и заголовки ответа типа контента.
- Проверить содержимое ответа содержит строку «привет»
4. Тестирование GET для получения ресурсов
Теперь, когда мы увидели основные шаги, связанные с созданием тестов. Давайте протестируем простой Fruit API, который мы представили в отличной статье Jersey MVC Support .
4.1. Получите обычный JSON
В приведенном ниже примере мы работаем с телом ответа как со стандартной строкой JSON:
@Test
public void givenFruitExists_whenSearching_thenResponseContainsFruit() {
final String json = target("fruit/search/strawberry").request()
.get(String.class);
assertThat(json, containsString("{\"name\":\"strawberry\",\"weight\":20}"));
}
4.2. Получить сущность вместо JSON
Мы также можем сопоставить ответ непосредственно с классом сущности Resource, например:
@Test
public void givenFruitExists_whenSearching_thenResponseContainsFruitEntity() {
final Fruit entity = target("fruit/search/strawberry").request()
.get(Fruit.class);
assertEquals("Fruit name: ", "strawberry", entity.getName());
assertEquals("Fruit weight: ", Integer.valueOf(20), entity.getWeight());
}
На этот раз мы указываем тип Java, в который объект ответа будет преобразован в методе get — объект
Fruit
.
5. Тестирование POST для создания ресурсов
Чтобы создать новый ресурс в нашем API, мы будем использовать POST-запросы. В следующем разделе мы увидим, как протестировать эту часть нашего API.
5.1. Сообщение в формате JSON
Давайте начнем с публикации простой строки JSON, чтобы проверить создание нового фруктового ресурса:
@Test
public void givenCreateFruit_whenJsonIsCorrect_thenResponseCodeIsCreated() {
Response response = target("fruit/created").request()
.post(Entity.json("{\"name\":\"strawberry\",\"weight\":20}"));
assertEquals("Http Response should be 201 ", Status.CREATED.getStatusCode(), response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit saved : Fruit [name: strawberry colour: null]"));
}
В приведенном выше примере мы используем метод post
, который принимает параметр объекта Entity .
Мы используем удобный метод json
для создания сущности из соответствующей строки JSON .
5.2. Опубликовать объект вместо JSON
Как мы уже видели с запросами на получение, мы также можем напрямую опубликовать класс сущности Resource, например:
@Test
public void givenCreateFruit_whenFruitIsInvalid_thenResponseCodeIsBadRequest() {
Fruit fruit = new Fruit("Blueberry", "purple");
fruit.setWeight(1);
Response response = target("fruit/create").request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(fruit, MediaType.APPLICATION_JSON_TYPE));
assertEquals("Http Response should be 400 ", 400, response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit weight must be 10 or greater"));
}
На этот раз мы используем метод сущности
для публикации нашей сущности Fruit, а также указываем тип мультимедиа как JSON.
5.3. Отправка форм с использованием POST
В нашем последнем примере публикации мы увидим, как тестировать отправку формы через почтовый запрос:
@Test
public void givenCreateFruit_whenFormContainsNullParam_thenResponseCodeIsBadRequest() {
Form form = new Form();
form.param("name", "apple");
form.param("colour", null);
Response response = target("fruit/create").request(MediaType.APPLICATION_FORM_URLENCODED)
.post(Entity.form(form));
assertEquals("Http Response should be 400 ", 400, response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit colour must not be null"));
}
Точно так же мы используем класс Entity
, но на этот раз передаем форму, содержащую ряд параметров, в наш почтовый запрос.
6. Тестирование других HTTP-команд
Иногда нам нужно протестировать другие конечные точки HTTP, такие как PUT и DELETE. Это, конечно, вполне возможно, используя инфраструктуру тестирования Джерси.
Давайте посмотрим на простой пример PUT:
@Test
public void givenUpdateFruit_whenFormContainsBadSerialParam_thenResponseCodeIsBadRequest() {
Form form = new Form();
form.param("serial", "2345-2345");
Response response = target("fruit/update").request(MediaType.APPLICATION_FORM_URLENCODED)
.put(Entity.form(form));
assertEquals("Http Response should be 400 ", 400, response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit serial number is not valid"));
}
После того, как мы вызвали метод запроса
, мы можем вызвать любой метод HTTP для текущего объекта запроса.
7. Дополнительные возможности
Инфраструктура тестирования Джерси содержит ряд дополнительных свойств конфигурации, которые могут помочь в отладке и тестировании.
В следующем примере мы увидим, как программно включить функцию с заданным именем:
public class FruitResourceIntegrationTest extends JerseyTest {
@Override
protected Application configure() {
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
//...
Когда мы создаем и настраиваем наше тестируемое приложение на Джерси. Мы также можем включить дополнительные свойства. В этом случае мы включаем два свойства ведения журнала — LOG_TRAFFIC
и DUMP_ENTITY
, которые будут предоставлять полезную дополнительную информацию для ведения журнала и отладки во время тестовых прогонов.
8. Поддерживаемые контейнеры
Как мы уже упоминали, контейнер де-факто, используемый при написании тестов с помощью Jersey Test Framework, — это Grizzly. Однако поддерживается ряд других контейнеров:
- Контейнер в памяти
- HttpServer из Oracle JDK
- Простой контейнер (org.simpleframework.http
- Контейнер Jetty (org.eclipse.jetty)
Для получения дополнительной информации о том, как настроить эти контейнеры, см. документацию здесь .
9. Заключение
Подводя итог, в этом руководстве мы изучили среду тестирования Джерси. Во-первых, мы начали с того, что рассказали, как настроить инфраструктуру тестирования Джерси, а затем увидели, как написать тест для очень простого API.
В следующем разделе мы увидели, как писать тесты для различных конечных точек GET и POST API. Наконец, мы рассмотрели некоторые дополнительные функции и контейнеры, которые поддерживает инфраструктура тестирования Джерси.
Как всегда, полный исходный код статьи доступен на GitHub .