От і дісталися до тестування контролерів. Контролери, як знаєте, відповідають за обробку 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. Це не тільки корисно для забезпечення якості, а й класно виглядає в резюме. У наступній лекції зосередимося на тестуванні сервісів і репозиторіїв. А поки — озбройтеся знаннями й ідіть тестувати свої контролери!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ