«1. Обзор

В этом руководстве мы рассмотрим инфраструктуру тестирования Джерси и посмотрим, как мы можем использовать ее для быстрого написания интеграционных тестов.

Как мы уже видели в предыдущих статьях, Jersey — это платформа с открытым исходным кодом для разработки веб-сервисов RESTful. Мы можем узнать больше о Джерси в нашей статье «Введение в создание 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, поэтому мы можем выполнять такие действия, как проверка код состояния, чтобы убедиться, что операция действительно прошла успешно, или работать с фактическим текстом ответа.

Давайте более подробно объясним, что мы делаем в приведенном выше примере:

  1. Send an HTTP GET request to ‘/greetings/hi’
  2. Check the HTTP status code and content type response headers
  3. Test the contents of the response contains the string “hi”

4. Тестирование GET для извлечения ресурсов

Теперь мы рассмотрели основные шаги, связанные с созданием тестов. Давайте протестируем простой Fruit API, который мы представили в отличной статье о поддержке MVC в Джерси.

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. Post Plain 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. Дополнительные функции

Среда тестирования Jersey содержит ряд дополнительных свойств конфигурации, которые могут помочь в отладке и тестировании.

В следующем примере мы увидим, как программно включить функцию с заданным именем:

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.