제육's 휘발성 코딩
article thumbnail
반응형

Mocking 이란?

Controller Test를 위해선 Mocking에 대해 알고 있어야 하는데요. Mock은 테스트를 위해 실제 객체를 사용하는 것처럼 테스트를 위해 만든 모형으로 가짜 객체를 의미합니다.

Mock을 이용해서 테스트하는 과정을 Mocking이라고 부르며, 웹 애플리케이션 환경에선 Servlet Container와 Dispatcher Servlet이 메모리에 로딩되지만, Mocking을 하면 실제 테스트 컨테이너를 사용하기 때문에 Mocking을 통해 의존성을 단절시킨 상태로 테스트할 수 있습니다. 

SpringBoot 환경에서 테스트 코드를 작성할 때 @SpringBootTest + @AutoConfigureMockMvc 또는 @WebMvcTest를 사용하는데요. 차이점을 가볍게 알아보고 가겠습니다. 

 

@WebMvcTest vs @AutoConfigureMockMvc 

@WebMvcTest vs @AutoConfigureMockMvc

@SpringBootTest의 경우 Mock Container 또는 Servlet Container 설정 가능합니다. default는 Mock Servlet을 사용하며, classpath에 WebFlux가 있거나 servlet APIs가 없으면 Application Context를 띄웁니다.


@WebMvcTest와 @AutoConfigureMockMvc는 같이 사용할 경우 java.lang.IllegalStateException: Configuration error: found multiple declarations of @BootstrapWith for test class 오류가 발생합니다.
그 이유는 서로 Mocking을 하면서 충돌이 발생했기 때문입니다.

 

@WebMvcTest(PostController.class)
public class PostControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private PostService postService;

    @MockBean
    private UserService userService; // 종속성 존재로 MockBean 등록
}
  • @WebMvcTest의 경우 @Service, @Repository 의존성을 배제하기 때문에 테스트에서 사용되지 않더라도, 실제로 사용되고 있는 종속 서비스가 있다면 다음과 같이 @MockBean으로 등록해야 합니다. 그렇지 않으면 Failed to load ApplicationContext 에러를 만날 수 있습니다.

 

@MockBean vs @Autowired

@Autowired의 경우 실제 빈을 찾아 주입해 주는 용도로 구현된 내용을 사용하게 됩니다. 

@MockBean의 경우 가짜 객체를 가져온 형태로 spring-boot-test의 Mockito와 결합하여 실제 로그인한 사용자 또는 인가되지 않은 사용자 등의 테스트가 가능합니다. @MockBean과 Mockito를 사용하여 @WithMockUser, @WithAnonymousUser, @WithUserDetails 등 사용자 관련 인증을 처리할 수 있습니다.

@WithMockUser 등 Security에서 사용되는 어노테이션은 포스팅을 참고 부탁드립니다.

 

Controller Test 예제 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.domain.Page;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
public class PostControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private PostService postService;
    
    @Test
    @WithMockUser
    void 포스트수정() throws Exception{

        String title = "title";
        String body = "body";

        // mocking
        when(postService.modify(eq(title), eq(body), any(), any()))
                .thenReturn(Post.fromEntity(PostEntityFixture.get("userName", 1, 1)));

        mockMvc.perform(put("/api/v1/posts/1")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsBytes(new PostModifyRequest(title, body)))
                ).andDo(print())
                .andExpect(status().isOk());
    }
}
  • @SpringBootTest와 @AutoConfigureMockMvc의 경우 @Service, @Repository 모두 메모리에 올리기 때문에 관련된 종속 빈 들의 정보를 가져올 수 있어서 추가 @MockBean이 필요하지 않습니다. 
  • org.mockito 라이브러리를 통해 로그인하지 않은 사용자, 로그인 한 사용자가 포스트 작성 등의 상황을 가정해서 테스트할 수 있습니다.

 

 

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^