Вот мы и добрались до тестирования контроллеров. Контроллеры, как вы знаете, отвечают за обработку HTTP-запросов и передачу данных между клиентом и бизнес-логикой приложения. Все хотят, чтобы их контроллеры работали безупречно, но кто-то должен это проверить. Спойлер: этим кем-то будете вы. Благо, у нас есть мощный инструмент — MockMvc.
Зачем тестировать контроллеры? Ну, во-первых, тестирование контроллеров позволяет быть уверенным, что ваши эндпоинты работают (и не только в вашем браузере).
Во-вторых, контроллеры часто являются первым элементом приложения, на который падает весь гнев пользователей в случае ошибок. Например, что сделает пользователь, если запрос по URI /api/users возвращает 500 Internal Server Error? Конечно, обвинит ваш код (и, возможно, вас лично). Так что, если ваш код работает, вы защищены.
Что такое MockMvc?
MockMvc — это инструмент, предоставляемый Spring Test, который позволяет тестировать поведение веб-приложений без необходимости развертывания реального сервера. Он имитирует обработку HTTP-запросов и позволяет проверять ответы, заголовки, статусы и даже ошибки.
MockMvc позволяет отправлять тестовые HTTP-запросы к вашим контроллерам и проверять, как они на них отвечают. Например, вы можете отправить GET-запрос на /api/users и проверить, что статус ответа — 200, а тело содержит ожидаемые данные.
Вот небольшой пример взаимодействия с MockMvc:
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(content().json("[{\"id\":1,\"name\":\"John Doe\"}]"));
Подготовка к тестированию
Прежде чем начать писать тесты, давайте убедимся, что у нас есть всё необходимое:
- Контроллер: напишем пример контроллера, который будем тестировать.
- Зависимости: добавим Spring Boot Test, JUnit и Mockito для работы.
- Конфигурация MockMvc: настроим MockMvc для тестов.
Шаг 1: Пример контроллера для тестирования
Предположим, у нас есть контроллер для работы с пользователями:
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
}
Контроллер содержит два эндпоинта:
GET /api/users— возвращает список пользователей.POST /api/users— добавляет нового пользователя.
Шаг 2: Настройка зависимостей в pom.xml
Добавим необходимые зависимости для тестирования:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
Шаг 3: Написание тестов для контроллера
Теперь создадим тестовый класс для UserController.
Импортируем нужные классы
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
Настроим класс тестов
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
// Здесь мы будем писать тесты
}
Шаг 4: Тестирование GET /api/users
Напишем тест для проверки, что GET /api/users возвращает корректный список пользователей.
@Test
public void shouldReturnListOfUsers() throws Exception {
// Создаем тестовые данные
List<User> users = List.of(new User(1L, "John Doe"), new User(2L, "Jane Doe"));
// Настраиваем поведение мока
when(userService.findAll()).thenReturn(users);
// Отправляем GET-запрос и проверяем результат
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()").value(users.size()))
.andExpect(jsonPath("$[0].name").value("John Doe"))
.andExpect(jsonPath("$[1].name").value("Jane Doe"));
}
Что здесь происходит:
- Создаем список пользователей для теста.
- Настраиваем мок
userService, чтобы он возвращал этот список при вызовеfindAll(). - Делаем GET-запрос на
/api/users. - Проверяем:
- Статус ответа (200 OK).
- Размер списка в JSON-ответе.
- Значение поля
nameу пользователей.
Шаг 5: Тестирование POST /api/users
Тестируем, что POST /api/users создаёт нового пользователя.
@Test
public void shouldCreateNewUser() throws Exception {
// Создаем тестового пользователя
User newUser = new User(null, "John Doe");
User savedUser = new User(1L, "John Doe");
// Настраиваем поведение мока
when(userService.save(any(User.class))).thenReturn(savedUser);
// Отправляем POST-запрос и проверяем результат
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John Doe\"}"))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("John Doe"));
}
Здесь:
- Мы отправляем POST-запрос с JSON-объектом.
- Проверяем, что:
- Статус ответа —
201 Created - Ответ содержит ID и имя нового пользователя.
- Статус ответа —
Полезные советы
- Не забывайте про edge cases: тестируйте не только позитивные сценарии, но и негативные. Например, что происходит, если отправить некорректные данные.
- Используйте удобные методы: MockMvc предоставляет множество удобных методов, таких как
jsonPath(),header()и т.д. - Чистите после себя: пусть ваши тесты не полагаются на состояние предыдущих тестов. Каждый тест должен быть независимым.
Что дальше?
Теперь вы умеете тестировать REST-контроллеры с использованием MockMvc. Это не только полезно для обеспечения качества, но и отлично смотрится в резюме. В следующей лекции мы сосредоточимся на тестировании сервисов и репозиториев. А пока, вооружитесь знаниями и отправляйтесь тестировать свои контроллеры!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ