개요
이 때까지 Service 테스트는 필수적으로 해왔는데 Controller 테스트는 postman으로 해보고 되면 되는구나하고 넘겼다.
과거의 나 반성해라!
근데 유닛 테스팅에 대해 공부하던 중 Controller 테스트도 하는 것이 무조건 좋다고 해서(Postman은 http에 비해 상당히 제약이 적기 때문이다.)
그래서 Controller 테스트 코드 작성한거랑 발생한 오류들에 대해 정리해볼 것이다.
Test 코드
Test 코드 어노테이션
@WebMvcTest(controllers = EpisodeController.class)
@MockBean(JpaMetamodelMappingContext.class)
@AutoConfigureMockMvc
먼저 Service 테스트 코드와 다르게 @SpringBootTest를 사용하지 않는다.
사실 사용해도 되긴하는데 @SpringBootTest를 사용하면 Spring boot 전역을 대상으로 하지만 @WebMvcTest는 웹 계층(Web Layer)만 테스트하기 때문에 JPA나 서비스 계층과 관련없이 작동하기 때문이다.
@MockBean
@Mock을 서비스 테스트에서 사용했는데 이걸 사용하니까 에러가 발생한다..
Failed to load ApplicationContext for [WebMergedContextConfiguration@312f3050 testClass
이걸 @MockBean으로 수정하면 해결된다.
- @Mock
- 단위 테스트에서 사용된다.
- Mock 객체를 직접 직접 생성하여 사용하기 때문에 스프링 컨텍스트와 상관없다.
- @MockBean
- 통합 테스트를 수행할 때 사용된다.
- Mock객체를 생성하고 스프링 컨텍스트에 등록된다. -> 관련 객체와 연결되어 통합 테스트를 진행할 수 있다.
이 차이 때문에 Web 계층에서 테스트할 때 @MockBean으로 등록해야 컨텍스트에 등록된 서비스나 다른 컴포넌트 클래스를 사용할 수 있다.
MockMvc
MockMvc를 이용하여 curl이나 RestTemplate처럼 api통신을 Mock객체로 할 수 있다.
MockMultipartHttpServletRequestBuilder requestBuilder;
requestBuilder = (MockMultipartHttpServletRequestBuilder) MockMvcRequestBuilders.multipart("/episode")
.header("Authorization", "Bearer test-token")
.contentType(MediaType.MULTIPART_FORM_DATA);
mockMvc.perform(requestBuilder
.file(image)
.file(audio)
.file(episodeDataFile)
.with(csrf()))
.andExpect(status().isOk())
.andExpect(content().string("2 created"));
테스트할 컨트롤러가 첨부파일을 받아서 저장하는 api이기 때문에 RequestBuilder 설정을 MockMultipartHttpServletRequestBuilder로 하여 multipartfile을 받도록 했다.
에러들
EnableJpaAuditing 문제
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-06-02T16:46:05.306+09:00 ERROR 444 --- [ Test worker] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaAuditingHandler': Cannot resolve reference to bean 'jpaMappingContext' while setting constructor argument
jpaAuditingHandler를 만들 수가 없단다....
원인
Springboot 코드에서 BaseEntity객체를 상속받아 각 엔티티를 생성할 때 생성 시간과 수정 시간을 묶어서 기록하게 해놨다.
여기가 문제였다!
MockMvcTest에서는 타겟을 웹 계층만 잡다보니 스프링 내부에 있는 @EnableJpaAuditing를 Bean에 등록하지 못해서 비었다고 나오는 것이었다..
해결
@WebMvcTest(controllers = EpisodeController.class)
@MockBean(JpaMetamodelMappingContext.class)
@AutoConfigureMockMvc
class EpisodeControllerTest {
@MockBean JpaMetamodelMappingContext를 넣어서 필요한 타겟들을 Bean에 등록하여 사용할 수 있게 설정한다!
403 에러
Status
Expected :200
Actual :403
<Click to see difference>
java.lang.AssertionError: Status expected:<200> but was:<403>
andExpect(status().isOk())에서 200을 예상했지만 403에러가 발생했다.
원인
CSRF문제가 원인이었다.
Post, Put, Delete 요청에 대해 CSRF 공격을 방지하기 위한 CSRF 토큰이 필요하다고 하는데 이 토큰이 MockMvcTest에서는 자동으로 생성이 안돼서 CSRF 토큰이 없어 403 에러가 발생했던 것이다.
// when, then
mockMvc.perform(requestBuilder
.file(image)
.file(audio)
.file(episodeDataFile)
.with(csrf())
)
.andExpect(status().isOk())
.andExpect(content().string("2 created"));
mockMvc 설정에 with(csrf())를 추가하여 CSRF 토큰을 넣어줘서 해결한다.
401 에러
Status
Expected :200
Actual :401
<Click to see difference>
java.lang.AssertionError: Status expected:<200> but was:<401>
403에러를 해결하고 실행해보니 이번엔 401에러가 발생했다.......
원인
특정 사용자 인증이 필요한 서비스 api라서 사용자가 필요하다.
이를 @WithMockUser 어노테이션을 사용하여 UsernamePasswordAuthenticationToken 타입의 Authentication 객체를 SpringContext 내에 생성한다. 이를 통해서 유저를 서버에 담아서 사용자 인증이 된 것처럼 작동시킬 수 있다.
@Test
@WithMockUser
void 에피소드_생성() throws Exception {
결론
성공했다!!!!!!!!
컨트롤러 테스팅은 기존 @SpringBootTest와는 사용하는 Mock타입이나 테스팅 타겟이 달라서 설정해야하는 부분이 생소하여 어려웠다.
그래도 성공은 했으니 ok입니다!
무작정 만들고 포스트맨 던지고 만들고 포스트맨 던지고 하는 것보다 유닛 테스트로 만들어서 성공 화면 띄우는 것이 더 성취감이 높은 것 같다.ㅎㅎ
사실 포스트맨으로 되면 된게 아닐까?
하기에는 성공 기록도 남기고 api 설정도 한번 돌아볼겸 작성하는 것이 무조건 좋은 것 같긴하다!
아마 post, get 등 여러 방식에 대해서 설정 방식 또한 다르기 때문에 다시 공부하고 적용해보면서 이해하고 사용해야 할 것 같다.
열심히, 열정적으로 한번 해보자!!!
출처
1. https://m.blog.naver.com/sosow0212/223076265261
'Dev > Spring' 카테고리의 다른 글
Springboot Argument Resolver 사용하기 (2) | 2024.09.01 |
---|---|
Spring Boot AWS S3 연결 (0) | 2024.04.27 |
@Builder 사용 시 List 초기화 NullPointException (0) | 2024.04.21 |
Spring Cloud Eureka Swagger 연결하기 (2) | 2024.04.15 |
JWT 리프레시 토큰 Cookie에 저장하기 (1) | 2024.03.28 |