← Назад к вопросам
Как обращался к Spring Controller при тестировании
2.3 Middle🔥 251 комментариев
#Spring Framework#Тестирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Тестирование Spring Controllers
Тестирование контроллеров требует разных подходов в зависимости от того, нужно ли тестировать HTTP слой или бизнес-логику.
1. MockMvc — тестирование HTTP слоя
Базовый setup
@WebMvcTest(UserController.class) // Загружает только контроллер и его зависимости
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetUser() throws Exception {
// Arrange
User user = new User(1L, "John", "john@example.com");
when(userService.getUserById(1L)).thenReturn(user);
// Act & Assert
mockMvc.perform(get("/api/v1/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("John"))
.andExpect(jsonPath("$.email").value("john@example.com"));
verify(userService).getUserById(1L);
}
}
Тестирование разных HTTP методов
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testCreateUser() throws Exception {
// POST запрос
mockMvc.perform(post("/api/v1/users")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"name": "Jane",
"email": "jane@example.com"
}
"""))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").exists());
}
@Test
void testUpdateUser() throws Exception {
// PUT запрос
mockMvc.perform(put("/api/v1/users/1")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"name": "John Updated",
"email": "john.updated@example.com"
}
"""))
.andExpect(status().isOk());
}
@Test
void testDeleteUser() throws Exception {
// DELETE запрос
mockMvc.perform(delete("/api/v1/users/1"))
.andExpect(status().isNoContent());
}
@Test
void testPartialUpdate() throws Exception {
// PATCH запрос
mockMvc.perform(patch("/api/v1/users/1")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"name": "John Patched"
}
"""))
.andExpect(status().isOk());
}
}
2. Тестирование параметров запроса
@WebMvcTest(UserController.class)
public class UserControllerParametersTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetUsersWithPagination() throws Exception {
// Query параметры
mockMvc.perform(get("/api/v1/users")
.param("page", "0")
.param("size", "10")
.param("sort", "name,asc"))
.andExpect(status().isOk());
// verify был вызван
verify(userService).findAllUsers(any(Pageable.class));
}
@Test
void testGetUsersByStatus() throws Exception {
// Multiple параметры
mockMvc.perform(get("/api/v1/users")
.param("status", "ACTIVE")
.param("role", "ADMIN"))
.andExpect(status().isOk());
}
@Test
void testGetUserWithPathVariable() throws Exception {
// Path variable
mockMvc.perform(get("/api/v1/users/john-doe"))
.andExpect(status().isOk());
}
}
3. Тестирование заголовков и аутентификации
@WebMvcTest(UserController.class)
public class UserControllerSecurityTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testAuthorizationHeader() throws Exception {
mockMvc.perform(get("/api/v1/users/1")
.header("Authorization", "Bearer token123"))
.andExpect(status().isOk());
}
@Test
void testCustomHeaders() throws Exception {
mockMvc.perform(get("/api/v1/users/1")
.header("X-Request-ID", "req-123")
.header("X-Trace-ID", "trace-456"))
.andExpect(status().isOk())
.andExpect(header().exists("X-Response-Time"));
}
@Test
@WithMockUser(username = "admin", roles = "ADMIN")
void testWithMockUser() throws Exception {
// Тест с аутентифицированным пользователем
mockMvc.perform(get("/api/v1/users"))
.andExpect(status().isOk());
}
@Test
void testUnauthorized() throws Exception {
mockMvc.perform(get("/api/v1/users/1"))
.andExpect(status().isUnauthorized());
}
}
4. Тестирование ошибок и исключений
@WebMvcTest(UserController.class)
public class UserControllerExceptionTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testUserNotFound() throws Exception {
when(userService.getUserById(999L))
.thenThrow(new UserNotFoundException("User not found"));
mockMvc.perform(get("/api/v1/users/999"))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.error").value("User not found"));
}
@Test
void testInvalidInput() throws Exception {
mockMvc.perform(post("/api/v1/users")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"name": "",
"email": "invalid-email"
}
"""))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors").isArray());
}
@Test
void testValidationError() throws Exception {
mockMvc.perform(post("/api/v1/users")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"email": "john@example.com"
}
"""))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors[0].field").value("name"));
}
}
5. Integration Testing с @SpringBootTest
@SpringBootTest // Загружает весь Application Context
@AutoConfigureMockMvc // Включает MockMvc
public class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private UserRepository userRepository;
@BeforeEach
void setUp() {
userRepository.deleteAll();
}
@Test
void testCreateAndRetrieveUser() throws Exception {
// 1. Создаём пользователя
mockMvc.perform(post("/api/v1/users")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"name": "John",
"email": "john@example.com"
}
"""))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").exists());
// 2. Проверяем, что пользователь в БД
MvcResult result = mockMvc.perform(get("/api/v1/users")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();
String content = result.getResponse().getContentAsString();
assertThat(content).contains("john@example.com");
}
}
6. REST Assured — альтернатива MockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerRestAssuredTest {
@LocalServerPort
private int port;
@BeforeEach
void setUp() {
RestAssured.port = port;
RestAssured.basePath = "/api/v1";
}
@Test
void testGetUser() {
given()
.header("Authorization", "Bearer token")
.when()
.get("/users/1")
.then()
.statusCode(200)
.body("name", equalTo("John"))
.body("email", equalTo("john@example.com"))
.time(lessThan(1000L));
}
@Test
void testCreateUser() {
given()
.contentType(ContentType.JSON)
.body(new UserRequest("Jane", "jane@example.com"))
.when()
.post("/users")
.then()
.statusCode(201)
.header("Location", notNullValue());
}
}
7. Тестирование WebClient (для асинхронных запросов)
@WebMvcTest(AsyncUserController.class)
public class AsyncUserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testAsyncEndpoint() throws Exception {
// Для async endpoints
mockMvc.perform(get("/api/v1/users/1/async"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
}
8. Пример комплексного теста
@WebMvcTest(PaymentController.class)
public class PaymentControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private PaymentService paymentService;
@Test
void testPaymentWorkflow() throws Exception {
// 1. Создаём платёж
MvcResult result = mockMvc.perform(
post("/api/v1/payments")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"amount": 99.99,
"currency": "USD",
"method": "CREDIT_CARD"
}
""")
.header("Authorization", "Bearer token"))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").exists())
.andReturn();
// 2. Извлекаем ID платежа
String paymentId = JsonPath.read(result.getResponse().getContentAsString(), "$.id").toString();
// 3. Получаем статус платежа
mockMvc.perform(get("/api/v1/payments/" + paymentId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("PENDING"));
// 4. Проверяем вызовы
verify(paymentService, times(1)).createPayment(any());
}
}
Лучшие практики
- Используй @WebMvcTest для тестирования только контроллеров
- Мокируй зависимости с @MockBean
- Тестируй HTTP статусы, заголовки, тело ответа
- Используй @SpringBootTest для интеграционных тестов
- Проверяй как позитивные, так и негативные сценарии
- Валидируй ошибки и исключения
- Используй описательные имена тестов
- Группируй тесты по функциональности