Тестирование Spring Boot приложения с TestRestTemplate

TestRestTemplate позволяет закодировать интеграционные тесты Spring-приложения. Этот класс запускает веб-сервер, делает запрос  – все по-настоящему, как если бы запрос делался внешним приложением. Задействуются все нижележащие слои.
Ниже мы рассмотрим примеры использования TestRestTemplate.

Что тестируем

Тестировать будем простой контроллер, описанный в статье Spring Boot REST API. Он предоставляет REST-сервис для операций получения, создания, редактирования и удаления сущности Person.

Тестовый класс и его аннотации

@after

Предполагается, что база данных для интеграционных тестов – отдельная, поэтому после каждого теста мы очищаем базу в методе resetDb():

@After
public void resetDb() {
  repository.deleteAll();
  repository.flush();
}

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)

Эта аннотация позволяет запускать тесты на случайном порту, а также создает бин TestRestTemplate, который мы можем просто внедрить в тестовый класс:

@Autowired
private TestRestTemplate restTemplate;

createTestPerson()

Этот метод вынесен отдельно, так как мы часто создаем Person в базе в начале тестов.

Вот так сокращенно выглядит тестовый класс (некоторые тесты перенесены ниже отдельно, весь код на GitHub):

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)

public class PersonControllerIntegrationTest {
  @Autowired
  private TestRestTemplate restTemplate;

  @Autowired
  private PersonRepository repository;

  @After
  public void resetDb() {
    repository.deleteAll();
    repository.flush();
  }

  @Test
  public void whenCreatePerson_thenStatus201() {

    Person person = new Person("Michail");

    ResponseEntity<Person> response = restTemplate.postForEntity("/persons", person, Person.class);

    assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
    assertThat(response.getBody().getId(), notNullValue());
    assertThat(response.getBody().getName(), is("Michail"));
  }

        //..другие тесты

  private Person createTestPerson(String name) {
    Person emp = new Person(name);
    return repository.saveAndFlush(emp);
  }

}

postForEntity() и getForEntity() – Получение JSON

Методы postForEntity(), getForEntity() позволяют вернуть ResponseEntity. Имея этот класс, можно проверить и код ответа, и тело ответа и все прочее:

@Test
public void whenCreatePerson_thenStatus201() {

  Person person = new Person("Michail");

  ResponseEntity<Person> response = restTemplate.postForEntity("/persons", person, Person.class);

  assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
  assertThat(response.getBody().getId(), notNullValue());
  assertThat(response.getBody().getName(), is("Michail"));
}

Тут мы проверяем, что Person создался, вновь созданный объект вернулся в JSON и код ответа 201(created).

getForObject(), postForObject()  – Получение Бина

Если нужно только тело ответа, то можно сделать еще проще. Метод getForObject() сразу преобразует JSON в объект:

@Test
public void givenPerson_whenGetPerson_thenStatus200() {
    long id = createTestPerson("Joe").getId();

    Person person = restTemplate.getForObject("/persons/{id}", Person.class, id);
    assertThat(person.getName(), is("Joe"));
}

Здесь мы получаем бин по идентификатору и проверяем, что поле name соответствует.

Обратите внимание, что параметры запроса прописываются в фигурных скобках и затем передаются как аргументы в самом конце в сигнатуре метода (столько штук, сколько надо).

exchange()

Редактирование Person будем тестировать с помощью метода exchange(). Это самый гибкий метод, в котором можно передать и тип запроса (GET, POST, PUT..), и HttpEntity, и параметры. Возвращает он ResponseEntity:

@Test
public void whenUpdatePerson_thenStatus200() {

  long id = createTestPerson("Nick").getId();
  Person person = new Person("Michail");
  HttpEntity<Person> entity = new HttpEntity<Person>(person);

  ResponseEntity<Person> response = restTemplate.exchange("/persons/{id}", HttpMethod.PUT, entity, Person.class,
        id);
  assertThat(response.getStatusCode(), is(HttpStatus.OK));
  assertThat(response.getBody().getId(), notNullValue());
  assertThat(response.getBody().getName(), is("Michail"));
}

 

Получение списка

Со списком не все так просто. Для того, чтобы не потерять тип элементов возвращаемого списка, мы должны создать и передать экземпляр анонимного класса:

new ParameterizedTypeReference<List<Person>>() {}

Весь код такой:

@Test
public void givenPersons_whenGetPersons_thenStatus200() {
  createTestPerson("Joe");
  createTestPerson("Jane");
  ResponseEntity<List<Person>> response = restTemplate.exchange("/persons", HttpMethod.GET, null,
    new ParameterizedTypeReference<List<Person>>() {
    });
  List<Person> persons = response.getBody();
  assertThat(persons, hasSize(2));
  assertThat(persons.get(1).getName(), is("Jane"));
}

Здесь мы создаем два элемента Person в базе, получаем список и проверяем, что он состоит из двух элементов. Также тестируем поле name второго элемента.

TestRestTemplate vs. MockMvc

MockMvc не запускает сервер, в остальном же оба эти класса дают возможность написать интеграционные тесты.

С MockMvc  можно еще использовать аннотации, которые создают бины только для веб-слоя, а остальные слои можно сэмулировать.

Итог

Полный исходный код примеров можно скачать на GitHub.

Тестирование этого же приложения с помощью REST-assured описано здесь.

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *