[트러블슈팅/개념정리]

입력하다

컨트롤러를 테스트하기 위해 코드를 열심히 작성했습니다.

제 기억력에 의지해서 썼는데… 실패했을 뿐만 아니라 컨텍스트로더도 전혀 작동하지 않아서 몇 시간 동안 해결책을 찾다가 해결했습니다. 소요된 시간에 비해 무의미한 양의 문제 해결이었지만 테스트 주석을 배울 수 있는 기회였습니다.

직감과 직감으로 작성된 거친 테스트 코드

잘못된 테스트 코드

@WebMvcTest
@Import( {TestSecurityConfig.class, TestJpaConfig.class} )
@ExtendWith(SpringExtension.class)
@AutoConfigureRestDocs
public class FeedbackControllerTest {
    @Autowired
    MockMvc mockMvc;
    @Autowired
    ObjectMapper objectMapper;
    @InjectMocks
    FeedbackController feedbackController;
    @Mock
    FeedbackServiceImpl feedbackService;
    @Test
    @DisplayName(" 요청 인입 시 정상적으로 리턴한다 ")
    void writeFeedback_suc() throws Exception {
    //... 이하 생략
}


사람이 대충 코드를 작성하면 이런 욕먹을텐데…공식 문서를 읽어보자


이미지를 클릭하시면 공식문서로 이동합니다

불친절하고 읽을 수 없습니다 …

적어도 밑줄 친 부분만 해석한다면 웹 레이어를 테스트하는 데 필요한 컨트롤러와 변환기만 제공하는 간단한 테스트 주석입니다.그러고 보니 그렇네요.. SpringSecurity 및 MockMvc 설정도 자동입니다.한다고 한다

@MockBean, @Import와 같이 쓰라고 되어있는데 @Mock으로 2시간째 놀고 있습니다.

더보기

공식 문서 및 가이드를 확인하려면 여기를 클릭하십시오.


암튼.. 뒷면에 검사할 대상을 쓰라고 되어있다.

내가 이것을 말할 때 누가 이해합니까?


지침을 보려면 여기를 클릭하십시오.

죄송합니다, 설명서에 있습니다^^;;

변경된 코드

@WebMvcTest( FeedbackController.class )
@Import( TestSecurityConfig.class )
@ExtendWith(SpringExtension.class)
@AutoConfigureRestDocs
public class FeedbackControllerTest {
    @Autowired
    MockMvc mockMvc;
    @Autowired
    ObjectMapper objectMapper;
    @MockBean
    FeedbackServiceImpl feedbackService;
    @MockBean
    ModelMapper modelMapper;
    @Test
    @DisplayName(" 요청 인입 시 정상적으로 리턴한다 ")
    void writeFeedback_suc() throws Exception {
       //test code
    }
}

변경된 사항

1. @InjectMocks 삭제
FeedbackController는 WebMvcTest에 이미 테스트 대상이라고 알려줬기 때문에 삭제할 수 있습니다.

2. @Mock 대신 @MockBean

공식 문서에 명시된 바와 같이 MockBean은 종속 클래스를 Bean으로 대체하는 데 사용되었습니다.

그렇다면 @ExtendWith 및 @InjectMocks와 같은 나머지 주석을 정리하겠습니다.

@ExtendWith(SpringExtension.class)

스프링 기능은 JUnit 테스트에서도 사용할 수 있습니다.

package org.springframework.test.context.junit.jupiter;

그래서 JUnit에서 제공하는 클래스입니다.


참고로 내부에 이런 메소드가 있습니다.


@Autowired가 내부적으로 사용되는지 확인하고(메서드에는 @Autowired가 추가되지 않아야 함) 이중 확인을 피하기 위해 캐시하는 기능입니다.

@InjectMocks

Mockito 프레임워크의 주석오전.

예를 들어 제품 서비스가 제품 리포지토리에 의존하는 경우

@InjectMocks ProductService productService;
@Mock ProductRepository productRepository;

이렇게 쓸 수 있습니다

WebMvsTest와 Mock을 함께 사용할 수 없는 이유는 무엇입니까?

기본적으로 WebMvcTest는 Spring Framework에 속합니다. 그래서 콩을 사용

반면에, Mockito는 Spring 애플리케이션 컨텍스트와 독립적입니다. 그래서 콩을로드하지 마십시오

즉, WebMvcTest에서는 @Mock을 사용할 수 있는 경우가 없고 @Mock을 추가하더라도 SpringContext에 없기 때문에 사용할 수 없다.

그래서 Mock 객체를 생성하고자 한다면 @MockBean을 작성하여 @WebMvcTest에 빈으로 등록할 수 있도록 해야 한다.

나가

요약하자면,

1. 컨트롤러 테스트를 하고자 한다면 통합 테스트가 아닌 이상 @WebMvcTest를 조심하는 것이 좋다.

2. 이때 @WebMvcTest(Target Class.Class) 형식으로 작성한다.

3. 고립된 테스트를 하고 싶다면 @Mock이 아닌 @MockBean을 사용해야 합니다.

3-1 그 이유는 @WebMvcTest 자체가 스프링 컨텍스트에서 일부 웹 레이어 관련 빈을 로드하고 테스트하는 원칙에 따라 작동하기 때문입니다. 즉, WebMvcTest가 인식하기 위해서는 bean으로 등록되어야 합니다.

참조

https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing

https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTest.html