
오늘은 컨트롤러 테스트를 붙이면서 생긴 문제를 해결하고, JWT 신규 플로우 테스트 방향(단위/통합)까지 정리했다. 과정에서 Spring Boot 4 변경점(@MockBean 제거 등)과 JPA Auditing이 @WebMvcTest를 깨는 케이스, ObjectMapper import 실수 같은 실전 트러블슈팅도 같이 정리했다.
예: Service 분기/예외, 도메인 상태전이, 유틸 계산
예: @WebMvcTest로 Controller + Validation + ExceptionHandler
예: Security 필터 + EntryPoint + Controller 연동 확인
mockMvc.perform(post("/api/auth/signup")
.contentType(MediaType.APPLICATION_JSON) // 요청 Content-Type: JSON
.content(objectMapper.writeValueAsString(req))) // request body JSON
.andExpect(status().isCreated()) // 201 검증
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) // JSON 응답 검증
.andExpect(jsonPath("$.id").value(1L)); // 응답 JSON 필드 검증
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@MockBean/@SpyBean이 제거(deprecate → removal)되면서 대체 애노테이션을 써야 한다.@MockitoBean / @MockitoSpyBean
JPA metamodel must not be empty
jpaAuditingHandler / jpaMappingContext 관련 BeanCreationException메인 클래스에 @EnableJpaAuditing이 붙어있어서
@WebMvcTest 같은 “웹 슬라이스 테스트”에서도 Auditing이 켜짐
그런데 @WebMvcTest는 JPA 엔티티 메타모델을 준비하지 않으므로 충돌 발생
@ConfigurationPropertiesScan
@SpringBootApplication
public class DeliveryPlatformApplication { ... }
@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig { }
실서비스/통합 테스트에서는 Auditing 사용
@WebMvcTest에서는 JPA 인프라를 강제 로딩하지 않게 분리
org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper;
그래서
given(authService.signup(req)).willReturn(res);
는 객체 동일성/equals에 의해 매칭이 깨질 수 있다.
따라서
given(authService.signup(any(SignupRequest.class))).willReturn(res);
처럼 타입 매칭으로 안정화.
추가로 전달값 검증이 필요하면:
ArgumentCaptor 또는 argThat 사용"httpStatus":"409 CONFLICT"
-> 즉, 서버 내부에선 HttpStatus.CONFLICT(enum)이지만 JSON 직렬화 시 문자열 "409 CONFLICT"로 나간다.
따라서 테스트에서 enum을 직접 비교하면 타입이 달라서 실패할 수 있다.
문자열로 검증하거나, status는 status().isConflict()로 검증하고 바디는 errorCode/message만 확인
iat 발급 시간
exp 만료 시간
long diffMillis = exp - iat;
long expectedMillis = Duration.ofMinutes(expireMinutes).toMillis();
assertThat(diffMillis).isBetween(expectedMillis - 1000, expectedMillis + 1000);
또한 subject는 String.valueOf(1L)이므로 "1"이다. "1L"이 아니다.
import static org.assertj.core.api.Assertions.assertThat;