JUnit5 + Mockito 이용해서 컨트롤러 테스트 하기

Kim Dong Kyun·2023년 7월 21일
1

Today I learned

목록 보기
43/43
post-thumbnail
post-custom-banner

개요

나는 지금 새로운 프로젝트를 진행중이다.

  • 그리고, 이전 프로젝트(토이 프로젝트)에서는 JUnit 5 + Mockito 를 사용해서 컨트롤러 테스트를 진행 할 때 아래와 같은 방식으로 진행했다.
@WebMvcTest(MyMovieController.class)
class MyMovieControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private MyMovieService myMovieService;

    @Test
    void getMovieListNoContentTest() throws Exception {
        // given
        Long pageNum = 10L;

        // when
        List<MovieResponseDto> movieList = new ArrayList<>();

        when(myMovieService.getMoviesPaging(pageNum)).thenReturn(movieList);

        // then
        mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/movies/pages/{pageNum}", pageNum)
                        .contentType(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isNoContent())
                .andReturn();

        verify(myMovieService, times(1)).getMoviesPaging(pageNum);
    }
}

1. @WebMvcTest

Application Context에서 "모든" 컴포넌트를 스캔하는 것이 아니라,

@WebMvcTest(Target.class)

위와 같이 목표로하는 클래스만 스캔하도록 해주는 어노테이션이다.

  • @SpringBootTest 어노테이션은 전체 컴포넌트를 스캔하므로 성능상 유리하다.

  • Target 클래스가 주입받아야 할 객체는 @MockBean 어노테이션을 사용해서 의존 주입 받는다(가짜 객체 형태로)

Service, Repository dependency가 필요한 경우에는 @MockBean으로 주입받아 테스트를 진행 한다.

2. MockMvc

  • 주로 컨트롤러단에서의 테스트를 위해 사용한다. 하지만 근본적인 의미로는 애플리케이션을 런 시키지 않고도 MVC 동작을 테스트 할 수 있다.
  • mockMvc 객체의 매서드로 가장 많이 사용했던 것이 .perform() 매서드이다. 라인별로 뜯어보자

mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/movies/pages/{pageNum}", pageNum)
                        .contentType(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isNoContent())
                .andReturn();

  1. MockMvcRequestBuilders? -> 스프링 공식 문서

위 문서에서 해당 클래스의 매서드들을 더 자세히 알 수 있다.

  1. .get()?
  • get, post, delete 와 더불어 request(HTTPMethod, URI) 와 같은 형식으로도 사용이 가능하다.
  1. .contentType
  • 요청의 헤더에 존재하는 Content-Type 을 String 값으로 설정한다.

  • 위에서는 JSON

  1. andExpect()
  • 패러미터로 ResultMatcher 가 들어간다

RequestMatcher? ResultMatcher ?

  • RequestMatcher는 예상되는 클라이언트 요청을 정의하는 데 사용된다.
  • ResultMatcher는 Spring의 MockMvc를 사용한 테스트 중에 컨트롤러가 생성한 응답에 대한 조건을 설정 할 수 있도록 한다. (주로 Status, Content등을 "검증"한다.)

발단

위와 같이 이전에 했던대로 @WebMvcTest() 어노테이션을 통해 타겟을 정하고 테스트 하려는데, 문제가 생겼다.

위와 같은 형태로 인증 객체를 불러오는 과정(UserDetailsImpl 의 getUser() 매서드)에서, user가 null이 들어갔던 것.

시도 1

그렇다면, MockBean 으로 UserDetails 를 의존주입해서 사용한다면?

마찬가지로 예외가 발생한다.

시도 2, 해결

@BeforeEach
    void setUp(){
        user = mock(User.class); // user 목 객체 생성
        when(user.getId()).thenReturn(1L); // 유저의 아이디는 1로
        userDetails = mock(UserDetailsImpl.class);
        when(userDetails.getUser()).thenReturn(user);  // userDetailsImpl 에서 getUSER() 호출 시 위 유저 반환하도록 설정
        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(new UsernamePasswordAuthenticationToken(userDetails, null, null));
    }

위와 같이, SecurityContext 에서 Authentication 을 명시적으로 설정 해 주면 필터를 잘 타면서 테스트가 완료된다.

post-custom-banner

0개의 댓글