Spring Boot/Test Code

Junit5] WebMvcTest

나른한 찰리 2023. 6. 9. 14:58
반응형

@WebMvcTest 어노테이션을 사용한 테스트 코드 작성

1. UserControllerTest

@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private UserService userService;

    @Test
    @DisplayName("닉네임 검증; 공백 체크")
    public void testNicknameDuplicateBlank() throws Exception {
        mvc.perform(get("/users/check-nickname")
                        .queryParam("nickname", "  "))
                .andExpect(status().isBadRequest())
                .andDo(print());
    }
}
    1. @MockBean을 사용해서 UserService를 주입하지 않으면 오류 발생한다.  [SpringBoot] @WebMvcTest 단위 테스트시 Bean 주입 에러
    2. .andDo(print()) 를 통해 요청/응답 메시지 전체를 확인할 수 있다.

 

내가 기대한 결과

  • .andExpect(status().isBadRequest()) 의 결과는 400이므로, 테스트가 성공적으로 수행되어야 한다.
{
    "status": 400,
    "errorName": "BAD_REQUEST",
    "message": "Nickname only contains blank",
    "code": "U002"
}

 

실제 결과

  • 401 응답이 출력되어, 400과 다르므로 테스트가 실패한다.
MockHttpServletResponse:
           Status = 401
    Error message = Unauthorized
          Headers = [WWW-Authenticate:"Basic realm="Realm"", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

 

오류 해결

Spring Security REST - Unit Tests fail with HttpStatusCode 401 Unauthorized

@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = UserController.class, excludeAutoConfiguration = {SecurityAutoConfiguration.class})
class UserControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private UserService userService;

    @Test
    @DisplayName("닉네임 검증; 공백 체크")
    public void testNicknameDuplicateBlank() throws Exception {
        mvc.perform(get("/users/check-nickname")
                        .queryParam("nickname", "  "))
                .andExpect(status().isBadRequest())
                .andDo(print());
    }
}

 

 

2. @WebMvcTest는 Service나 Repository class를 주입받을 수 없다.

테스트할 때 MockMvc 주입받는 방법

따라서 Controller만 테스트할 경우에 사용한다. 이 때, @WebMvcTest를 사용하면 MockMvc를 주입받아서 사용할 수 있다.

 

 

 

 

3. java.lang.IllegalStateException: Failed to load ApplicationContext 오류 해결

  • JWT 인증 처리를 위해 JwtAuthenticationFilter를 생성한 뒤, 기존의 UserControllerTest를 실행하였으나 오류가 발생하였다. 
  • JwtAuthenticationFilter 및 JwtTokenProvider를 @Component를 통해 Spring Bean에 등록한 상태.
  • 오류 메시지는 아래와 같다.

 

3-1. 오류 원인

@WebMvcTest 는 컨트롤러와 관련된 Bean을 자동으로 configuration하며, 해당 Bean들은 아래와 같다. 

  1. @Controller
  2. @ControllerAdvice
  3. @JsonComponent
  4. Converter
  5. GenericConverter
  6. Filter
  7. HandlerInterceptor
  8. WebMvcConfigurer
  9. HandlerMethodArgumentResolver

JwtAuthenticationFilter 

@Slf4j
@Component
@RequiredArgsConstructor
@Order(1)
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        log.info(">> Enter the JwtAuthenticationFilter");
        String jwtToken = parseJwt(request);

//        if (jwtToken != null && jwtTokenProvider.validateToken(jwtToken)) {
//            Authentication auth = jwtTokenProvider.getAuthentication(jwtToken);
//            SecurityContextHolder.getContext().setAuthentication(auth);
//        }
//
//        log.info("next Filter");
        filterChain.doFilter(request, response);
    }

    private String parseJwt(HttpServletRequest request) {
        String headerAuth = request.getHeader("Authorization");
        if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
            return headerAuth.substring(7);
        }
        return null;
    }
}

 

오류 원인은 아직 잘 모르겠다..

 

3-2. 해결 방법

1. JwtAuthenticationFilter에서, @Component 어노테이션 주석 처리 후 실행 

@Slf4j
//@Component
@RequiredArgsConstructor
@Order(1)
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
		// 생략
        
        filterChain.doFilter(request, response);
    }
}

 

2. UserControllerTest에서, excludeFilter를 통해 JwtAuthenticationFilter 제외시킴

@MockBean(JpaMetamodelMappingContext.class)
@WebMvcTest(controllers = UserController.class, excludeAutoConfiguration = {SecurityAutoConfiguration.class},
        // 아래 내용 추가
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JwtAuthenticationFilter.class)})
@TestPropertySource(locations = "classpath:application-jwt.properties")
class UserControllerTest {

		// 생략
        
        
}

 

 

반응형