Рубрики
Без рубрики

Руководство по модульному тестированию API-интерфейсов Spring Boot REST

В этом уроке мы рассмотрим, как выполнять модульное тестирование API-интерфейсов Spring Boot REST с примерами, используя JUnit, Mockito и MockMvc.

Автор оригинала: Rayven Yor Esplanada.

Вступление

Тестирование системы является важным этапом в Жизненном цикле разработки программного обеспечения (SDLC) . Тестирование повышает надежность кода, надежность и гарантирует высокое качество программного обеспечения, поставляемого клиентам, если оно реализовано правильно.

Тестированию придается все большее значение с тех пор, как Разработка на основе тестов (TDD) стала заметным процессом в разработке программного обеспечения. Разработка на основе тестирования предполагает преобразование требований в тестовые наборы и использование этих тестовых наборов для обеспечения качества кода привратника. Код будет считаться неприемлемым, если он не пройдет ни один из тестовых случаев, объявленных в системе, и чем больше тестовых случаев будет соответствовать требованиям продукта, тем лучше. Кодовая база значительно расширена, но подтверждает тот факт, что система отвечает заданным требованиям.

API-интерфейсы REST обычно тщательно тестируются во время интеграционного тестирования . Однако хороший разработчик должен протестировать конечные точки REST еще до интеграции в свои Модульные тесты , поскольку они являются жизненно важной частью кода , поскольку это единственная точка доступа каждого объекта, желающего использовать службы на сервере.

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

Требования

Для этого руководства вам понадобятся следующие спецификации:

  • Пружинный ботинок v2.0+
  • JDK v1.8+
  • JUnit 5 – Самый популярный и широко используемый фреймворк для тестирования Java.
  • Mockito – Фреймворк общего назначения для насмешек и заглушения сервисов и объектов.
  • MockMvc – Модуль Spring для выполнения интеграционного тестирования во время модульного тестирования .
  • Ломбок – Удобная библиотека для сокращения шаблонного кода.
  • Любая среда IDE, поддерживающая Java и Spring Boot (IntelliJ, VSC, NetBeans и т.д.)
  • Почтальон, curl или любой HTTP-клиент

Если вам все еще не совсем удобно создавать API REST с помощью Spring Boot – прочитайте наше руководство по созданию API REST для Spring Boot .

Мы будем использовать Ломбок в качестве удобной библиотеки, которая автоматически генерирует геттеры, сеттеры и конструкторы, и это полностью необязательно.

Настройка проекта

Самый простой способ начать работу с проектом загрузки со скелетной пружиной-это Spring Initializr :

Помимо этого, нам понадобится несколько дополнительных зависимостей, добавленных в pom.xml файл.

Добавление Зависимостей Модульного Тестирования

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

Для JUnit 5 , последней версии, нам нужно будет исключить JUnit 4 из зависимости spring-boot-starter-test , поскольку по умолчанию он добавляет JUnit 4. Чтобы добавить JUnit 5 в свой проект, добавьте junit-jupiter-engine в свои зависимости под вашим основным pom.xml файл после исключения JUnit 4 из springboot-starter-теста |/зависимости.

MockMvc уже включен в spring-boot-starter-test по умолчанию, звук>spring-boot-starter-тест, если вы исключите его и используете другое его исполнение, тогда все в порядке:



  org.springframework.boot
  spring-boot-starter-test
  test
  
    
      junit
      junit
    
  



  org.junit.jupiter
  junit-jupiter-engine
  test

Помимо JUnit 5, нам также необходимо добавить зависимости, чтобы включить Mockito в вашей системе. Для этого просто добавьте mockito-core в свои зависимости и укажите значение test в качестве области действия для этой зависимости:


  org.mockito
  mockito-core
  test


Примечание: Если вы не укажете версию для своих зависимостей, он просто получит последнюю доступную стабильную версию этой зависимости из репозитория, из которого вы загружаете.

Теперь мы можем приступить к кодированию уровней домена и сохраняемости.

Уровни домена и сохраняемости

Доменный уровень – Создание модели записи пациента

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

Не забудьте аннотировать свой класс модели с помощью @Entity , чтобы указать, что класс сопоставлен с таблицей в базе данных. Также можно указать аннотацию @Table , чтобы убедиться, что класс указывает на правильную таблицу.

Помимо этих двух аннотаций, включите аннотации утилиты Lombok ( @Data, @No/AllArgsConstructor, @Builder ), чтобы вам не нужно было объявлять ваши геттеры, сеттеры и конструкторы, поскольку Lombok уже делает это за вас.

Поля String и Integer помечены @NonNull , чтобы они не имели null или пустое значение для целей проверки:

@Entity
@Table(name = "patient_record")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PatientRecord {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long patientId;
    
    @NonNull
    private String name;
 
    @NonNull
    private Integer age;
    
    @NonNull 
    private String address;
}

Уровень сохраняемости – Создание хранилища записей пациентов

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

Давайте аннотируем интерфейс с помощью @Репозитория и расширим JpaRepository , чтобы создать правильно работающий интерфейс JpaRepository. В этом руководстве у JpaRepository не будет никаких пользовательских методов, поэтому тело должно быть пустым:

@Repository
public interface PatientRecordRepository extends JpaRepository {}

Теперь, когда мы создали наш простой домен и уровень сохраняемости, давайте перейдем к кодированию компонентов для вашего бизнес-уровня.

Бизнес-уровень

Бизнес – уровень состоит из контроллеров, которые обеспечивают связь с сервером и обеспечивают доступ к предоставляемым им службам.

Для этого урока давайте создадим контроллер, который предоставляет 4 простых конечных точки REST, по одной для каждой операции CRUD: Создание, чтение, обновление и Удаление .

Создание экземпляра класса контроллера – Контроллер записи пациентов

Во-первых, аннотируйте свой класс контроллера аннотацией @RestController , чтобы сообщить DispatcherServlet , что этот класс содержит методы сопоставления запросов.

Если вы раньше не работали с контроллерами Rest , прочитайте наше руководство по Аннотации @Controller и @RestController .

Чтобы предоставить услуги CRUD для методов, объявите Репозиторий записей пациентов интерфейс в классе контроллера и аннотируйте его с помощью @Autowired , чтобы неявно внедрить объект, чтобы вам не нужно было создавать его вручную.

Вы также можете аннотировать класс @RequestMapping свойством value , чтобы инициализировать базовый путь для всех методов сопоставления запросов в классе. Давайте установим для свойства значение значение запись пациента/|, чтобы базовый путь был интуитивно понятным:

@RestController
@RequestMapping(value = "/patient")
public class PatientRecordController {
    @Autowired PatientRecordRepository patientRecordRepository;
    // CRUD methods to be added
}

Теперь давайте создадим несколько методов, которые составляют функциональность CRUD, которую мы будем модульно тестировать.

Получение пациентов – Обработчик запроса GET

Давайте создадим два разных метода GET : один для получения всех записей пациентов в базе данных, а другой для получения одной записи с идентификатором пациента.

Чтобы указать , что метод сопоставляется с помощью GET , аннотируйте его аннотацией @GetMapping . Если вы не знакомы с производными вариантами @RequestMapping – вы можете прочитать наше руководство по Весенним аннотациям: @RequestMapping и его варианты :

@GetMapping
public List getAllRecords() {
    return patientRecordRepository.findAll();
}

@GetMapping(value = "{patientId}")
public PatientRecord getPatientById(return patientRecordRepository.findById(patientId).get();
}

Поскольку методу get Patient По идентификатору() требуется параметр ( Идентификатор пациента ), мы предоставим его через путь, аннотировав его с помощью @PathVariable и указав значение свойство переменной. Кроме того, установите свойство value аннотации @GetMapping , чтобы сопоставить переменную пути с ее фактическим местом в базовом пути.

Создание Обработчика запросов Пациентов после отправки

Для добавления новых записей пациентов потребуется метод POST -сопоставления. Метод примет Запись пациента параметр, аннотированный @RequestBody и @Действительный . Аннотация @Valid гарантирует, что все ограничения в базе данных и в классе сущностей будут перепроверены перед обработкой данных.

Если вы не знакомы с процессом десериализации HTTP – запросов к объектам Java-прочитайте наше руководство о том, как получить тело HTTP-сообщения в Spring Boot с помощью @RequestBody:

@PostMapping
public PatientRecord createRecord(@RequestBody @Valid PatientRecord patientRecord) {
    return patientRecordRepository.save(patientRecord);
}

Прежде чем перейти к другим методам запроса, давайте создадим одно общее исключение для всех исключений, встречающихся в базе кода, и назовем его Исключение InvalidRequestException . Для кода состояния давайте использовать BAD_REQUEST код состояния 400 .

Чтобы обработать исключения и преобразовать их в код состояния для возврата вызывающему, давайте объявим простой класс исключений, который расширяет класс RuntimeException :

@ResponseStatus(HttpStatus.BAD_REQUEST)
class InvalidRequestException extends RuntimeException {
    public InvalidRequestException(String s) {
        super(s);
    }
}

Обновление Обработчика запросов пациентов – PUT

Для обработки обновлений – для метода PUT давайте аннотируем его @PutMapping и потребуем параметр, аннотированный @RequestBody , содержащий обновленную Запись пациента , аналогичную POST сопоставлению.

Мы хотим убедиться, что запись существует для целей проверки, используя идентификатор пациента . Поскольку это запрос PUT , запись, подлежащая обновлению, должна существовать в базе данных, в противном случае это недопустимый запрос. Кроме того, создайте исключение InvalidRequestException , если тело запроса или идентификатор пациента поле равно нулю :

@PutMapping
public PatientRecord updatePatientRecord(@RequestBody PatientRecord patientRecord) throws NotFoundException {
    if (patientRecord == null || patientRecord.getPatientId() == null) {
        throw new InvalidRequestException("PatientRecord or ID must not be null!");
    }
    Optional optionalRecord = patientRecordRepository.findById(patientRecord.getPatientId());
    if (optionalRecord.isEmpty()) {
        throw new NotFoundException("Patient with ID " + patientRecord.getPatientId() + " does not exist.");
    }
    PatientRecord existingPatientRecord = optionalRecord.get();

    existingPatientRecord.setName(patientRecord.getName());
    existingPatientRecord.setAge(patientRecord.getAge());
    existingPatientRecord.setAddress(patientRecord.getAddress());
	
    return patientRecordRepository.save(existingPatientRecord);
}

Удаление Пациентов – Обработчик запроса на УДАЛЕНИЕ

Теперь мы также захотим иметь возможность удалять пациентов. Этот метод будет помечен @DeleteMapping и примет параметр идентификатор пациента и удалит пациента с этим идентификатором, если он существует. Метод вернет исключение и 400 код статуса, если пациент не существует. Как и метод GET , который извлекает пациента по идентификатору, добавьте свойство value в аннотацию @DeleteMapping , а также @PathVariable :

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

@DeleteMapping(value = "{patientId}")
public void deletePatientById(@PathVariable(value = "patientId") Long patientId) throws NotFoundException {
    if (patientRecordRepository.findById(patientId).isEmpty()) {
        throw new NotFoundException("Patient with ID " + patientId + " does not exist.");
    }
    patientRecordRepository.deleteById(patientId);
}

Теперь наш бизнес-слой загрунтован и готов! Мы можем пойти дальше и написать для него модульные тесты. Если вы хотите прочитать более подробное руководство по созданию API REST в Spring Boot – прочитайте наше руководство по созданию API REST для Spring Boot .

Давайте перейдем к созданию модульных тестов для API REST в нашем классе контроллера с использованием JUnit , Mockito и MockMvc .

Модульное тестирование API-интерфейсов Spring Boot REST

MockMvc – это решение, позволяющее модульное тестирование веб-уровня . Обычно тестирование API-интерфейсов REST выполняется во время интеграционного тестирования, что означает, что приложение должно запускаться в контейнере, чтобы проверить, работают ли конечные точки или нет. MockMvc позволяет тестировать веб-уровень (т. е. бизнес-уровень или уровень контроллера) во время модульного тестирования с соответствующими конфигурациями, но без дополнительных затрат на развертывание приложения.

Наличие модульных тестов для веб-уровня также значительно увеличит охват тестового кода для вашего приложения и отразится в таких инструментах, как Sonar и JaCoCo .

Каталог модульных тестов обычно находится в том же исходном каталоге в каталоге test/java/package . По умолчанию файловая структура модульного теста будет выглядеть следующим образом:

Project:
├─src
  ├───main
  │   ├───java
  │   └───resources
  └───test
      └───java

Также хорошей практикой и стандартным соглашением является называть ваши тестовые классы так же, как и контроллеры, которые вы тестируете, с суффиксом – Test . Например, если мы хотим протестировать Контроллер записей пациентов , мы создадим класс Контроллер записей пациентов в соответствующем пакете в src/test/java .

Вместо аннотирования тестового класса с помощью @SpringBootTest мы будем использовать аннотацию @WebMvcTest , чтобы зависимости, которые будут загружены при запуске тестового класса, напрямую влияли на класс контроллера. Любые службы, репозитории и подключения к базам данных не будут настроены и загружены после запуска теста, поэтому вам придется имитировать все эти компоненты с помощью Mockito .

В этом случае нам нужно указать только один контроллер – PatientRecordController.class , для @WebMvcTest аннотации. Если в одном тестовом классе введено несколько контроллеров, разделите контроллеры запятой , и заключите их в пару фигурных скобок {} :

@WebMvcTest(PatientRecordController.class)
public class PatientRecordControllerTest {
    @Autowired
    MockMvc mockMvc;
    @Autowired
    ObjectMapper mapper;
    
    @MockBean
    PatientRecordRepository patientRecordRepository;
    
    PatientRecord RECORD_1 = new PatientRecord(1l, "Rayven Yor", 23, "Cebu Philippines");
    PatientRecord RECORD_2 = new PatientRecord(2l, "David Landup", 27, "New York USA");
    PatientRecord RECORD_3 = new PatientRecord(3l, "Jane Doe", 31, "New York USA");
    
    // ... Test methods TBA
}

Здесь мы объявили объект MockMvc и снабдили его аннотацией @Autowired , что разрешено в данном контексте , поскольку MockMvc настроен автоматически и является частью зависимостей, загружаемых для этого тестового класса. Мы также автоматически подключили ObjectMapper объект; это будет использоваться позже.

Интерфейс Хранилище записей пациентов используется во всех конечных точках API, поэтому мы издевались над ним с помощью @Mockbeat . Наконец, мы создали несколько Записей пациентов экземпляров для целей тестирования.

Модульное тестирование обработчиков запросов GET

Теперь мы можем продолжить и сделать наш первый тестовый случай – также известный как модульный тест . Мы будем тестировать метод getAllRecords () , наш обработчик запросов GET . Для каждого модульного теста мы создадим один метод, который будет проверять другой. Каждый модульный тест снабжен аннотацией @Test , чтобы JUnit мог подобрать их и поместить в список всех тестов, которые необходимо выполнить:

@Test
public void getAllRecords_success() throws Exception {
    List records = new ArrayList<>(Arrays.asList(RECORD_1, RECORD_2, RECORD_3));
    
    Mockito.when(patientRecordRepository.findAll()).thenReturn(records);
    
    mockMvc.perform(MockMvcRequestBuilders
            .get("/patient")
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$", hasSize(3)))
            .andExpect(jsonPath("$[2].name", is("Jane Doe")));
}

Метод Mockito когда().thenReturn() цепочка издевается над вызовом метода getAllRecords() в репозитории JPA, поэтому каждый раз, когда метод вызывается в контроллере, он возвращает указанное значение в параметре метода thenReturn () . В этом случае он возвращает список из трех заранее заданных записей пациентов вместо фактического вызова базы данных.

MockMvc.perform() принимает MockMvcRequest и издевается над вызовом API, заданным полями объекта. Здесь мы создали запрос через MockMvcRequestBuilders и указали только GET путь и ContentType свойство , поскольку конечная точка API не принимает никаких параметров.

После выполнения perform () , и Ожидание() методы впоследствии привязываются к нему и проверяются на соответствие результатам, возвращаемым методом. Для этого вызова мы установили 3 утверждения в методах andExpect () : что ответ возвращает 200 или код состояния OK , ответ возвращает список размеров 3 , и третий Запись пациента объект из списка имеет имя свойство Джейн Доу .

Методы со статическими ссылками здесь – JSONPath() , имеет размер() и является() принадлежат классам MockMvcResultMatchers и Сопоставителей соответственно:

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.Matchers.*;

Вы, конечно, можете статически ссылаться на них:

.andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(3)))
.andExpect(MockMvcResultMatchers.jsonPath("$[2].name", Matchers.is("Jane Doe")));

Хотя, если у вас много и вы ожидаете() заявлений, связанных вместе, – это довольно быстро станет повторяющимся и раздражающим.

Примечание: Все эти утверждения не должны быть неудачными для прохождения модульного теста. Выполнение этого кода приводит к:

Теперь давайте добавим еще один тестовый случай для метода getparentbyid () . Прямо под предыдущим модульным тестом мы можем написать новый:

@Test
public void getPatientById_success() throws Exception {
    Mockito.when(patientRecordRepository.findById(RECORD_1.getPatientId())).thenReturn(java.util.Optional.of(RECORD_1));

    mockMvc.perform(MockMvcRequestBuilders
            .get("/patient/1")
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$", notNullValue()))
            .andExpect(jsonPath("$.name", is("Rayven Yor")));
}

Здесь мы проверяем, является ли результат нулевым , утверждая, что это не , и проверяем, равно ли имя поле возвращаемого объекта "Rayven Yor" . Если мы сейчас запустим весь Класс контроллера записей пациентов|/, нас встретят:

Модульное тестирование обработчиков запросов POST

Теперь, когда мы протестировали способность API извлекать отдельные идентифицируемые записи, а также список всех записей, давайте проверим его способность сохранять записи . Обработчик POST запроса принимает POST запрос и отображает предоставленные значения в Запись пациента POJO с помощью @RequestBody аннотации. Наш тестовый модуль также примет JSON и сопоставит значения в Запись пациента POJO с помощью ObjectMapper , который мы подключали ранее. Мы также сохраним ссылку на возвращенный MockHttpServletRequestBuilder после того, как он будет сгенерирован MockMvcRequestBuilders , чтобы мы могли проверить возвращенные значения:

@Test
public void createRecord_success() throws Exception {
    PatientRecord record = PatientRecord.builder()
            .name("John Doe")
            .age(47)
            .address("New York USA")
            .build();

    Mockito.when(patientRecordRepository.save(record)).thenReturn(record);

    MockHttpServletRequestBuilder mockRequest = MockMvcRequestBuilders.post("/patient")
            .contentType(MediaType.APPLICATION_JSON)
            .accept(MediaType.APPLICATION_JSON)
            .content(this.mapper.writeValueAsString(record));

    mockMvc.perform(mockRequest)
            .andExpect(status().isOk())
            .andExpect(jsonPath("$", notNullValue()))
            .andExpect(jsonPath("$.name", is("John Doe")));
    }

Повторный запуск класса приводит к:

Модульное тестирование обработчиков запросов PUT

Обработчик PUT запроса имеет немного больше логики, чем два предыдущих. Он проверяет, предоставили ли мы идентификатор, что приводит к исключению, если он отсутствует. Затем он проверяет, действительно ли идентификатор принадлежит записи в базе данных, создавая исключение, если это не так. Только тогда он фактически обновляет запись в базе данных, если идентификатор не null и он действительно принадлежит записи.

Мы создадим три метода тестирования, чтобы проверить, работают ли все три аспекта этого метода: по одному для успешного выполнения и по одному для каждого из возможных ошибочных состояний:

@Test
public void updatePatientRecord_success() throws Exception {
    PatientRecord updatedRecord = PatientRecord.builder()
            .patientId(1l)
            .name("Rayven Zambo")
            .age(23)
            .address("Cebu Philippines")
            .build();

    Mockito.when(patientRecordRepository.findById(RECORD_1.getPatientId())).thenReturn(Optional.of(RECORD_1));
    Mockito.when(patientRecordRepository.save(updatedRecord)).thenReturn(updatedRecord);

    MockHttpServletRequestBuilder mockRequest = MockMvcRequestBuilders.post("/patient")
            .contentType(MediaType.APPLICATION_JSON)
            .accept(MediaType.APPLICATION_JSON)
            .content(this.mapper.writeValueAsString(updatedRecord));

    mockMvc.perform(mockRequest)
            .andExpect(status().isOk())
            .andExpect(jsonPath("$", notNullValue()))
            .andExpect(jsonPath("$.name", is("Rayven Zambo")));
}

Однако в тех случаях, когда либо входные данные неверны, либо база данных просто не содержит объект, который мы пытаемся обновить, приложение должно реагировать исключением. Давайте проверим это:

@Test
public void updatePatientRecord_nullId() throws Exception {
    PatientRecord updatedRecord = PatientRecord.builder()
            .name("Sherlock Holmes")
            .age(40)
            .address("221B Baker Street")
            .build();

    MockHttpServletRequestBuilder mockRequest = MockMvcRequestBuilders.post("/patient")
            .contentType(MediaType.APPLICATION_JSON)
            .accept(MediaType.APPLICATION_JSON)
            .content(this.mapper.writeValueAsString(updatedRecord));

    mockMvc.perform(mockRequest)
            .andExpect(status().isBadRequest())
            .andExpect(result ->
                assertTrue(result.getResolvedException() instanceof PatientRecordController.InvalidRequestException))
    .andExpect(result ->
        assertEquals("PatientRecord or ID must not be null!", result.getResolvedException().getMessage()));
    }

@Test
public void updatePatientRecord_recordNotFound() throws Exception {
    PatientRecord updatedRecord = PatientRecord.builder()
            .patientId(5l)
            .name("Sherlock Holmes")
            .age(40)
            .address("221B Baker Street")
            .build();

    Mockito.when(patientRecordRepository.findById(updatedRecord.getPatientId())).thenReturn(null);

    MockHttpServletRequestBuilder mockRequest = MockMvcRequestBuilders.post("/patient")
            .contentType(MediaType.APPLICATION_JSON)
            .accept(MediaType.APPLICATION_JSON)
            .content(this.mapper.writeValueAsString(updatedRecord));

    mockMvc.perform(mockRequest)
            .andExpect(status().isBadRequest())
            .andExpect(result ->
                assertTrue(result.getResolvedException() instanceof NotFoundException))
    .andExpect(result ->
        assertEquals("Patient with ID 5 does not exist.", result.getResolvedException().getMessage()));
}

Поскольку мы сопоставили исключение InvalidRequestException с @ResponseStatus(HttpStatus.BAD_REQUEST) , исключение приведет к тому, что метод вернет HttpStatus.BAD_REQUEST . Здесь мы протестировали способность нашего API REST возвращать соответствующие коды состояния при обнаружении либо ошибочных данных, либо когда кто-то пытается обновить несуществующую сущность.

Модульное тестирование обработчиков запросов на УДАЛЕНИЕ

Наконец, давайте протестируем функциональность нашего обработчика запросов DELETE – создадим тест на успешный результат и тест на неудачный результат:

@Test
public void deletePatientById_success() throws Exception {
    Mockito.when(patientRecordRepository.findById(RECORD_2.getPatientId())).thenReturn(Optional.of(RECORD_2));

    mockMvc.perform(MockMvcRequestBuilders
            .delete("/patient/2")
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk());
}

@Test
public void deletePatientById_notFound() throws Exception {
    Mockito.when(patientRecordRepository.findById(5l)).thenReturn(null);

    mockMvc.perform(MockMvcRequestBuilders
            .delete("/patient/2")
            .contentType(MediaType.APPLICATION_JSON))
    .andExpect(status().isBadRequest())
            .andExpect(result ->
                    assertTrue(result.getResolvedException() instanceof NotFoundException))
    .andExpect(result ->
            assertEquals("Patient with ID 5 does not exist.", result.getResolvedException().getMessage()));
}

Теперь давайте используем Maven для очистки проекта, его компиляции и запуска тестов.

Запуск программы с модульным тестированием

Во-первых, нам нужно добавить плагин Maven Surefire в pom.xml файл, чтобы мы могли запустить команду mvn clean test . Мы также добавим дополнительный тег конфигурации, чтобы включить PatientRecordControllerTest.java тестовый класс, чтобы включить его в тесты Maven:


    
        maven-surefire-plugin
        2.21.0
        
            
                PatientRecordControllerTest.java
            
        
    
    
    

Затем в каталоге нашего проекта, используя терминал, давайте запустим:

$ mvn clean test

Что приводит к:

[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.demo.PatientRecordControllerTest
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.504 s - in com.example.demo.PatientRecordControllerTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.633 s
[INFO] Finished at: 2021-05-25T19:51:24+02:00
[INFO] ------------------------------------------------------------------------

Вывод

В этом руководстве мы рассмотрели, как создать и протестировать API Spring Boot REST с функциями CRUD с использованием JUnit, Mockito и MockMvc.