Controller
SpringBoot에서 JUnit5를 사용하여 테스트 코드를 작성할 때, @SpringBootTest 어노테이션을 자주 쓰는데, 상황에 따라 @WebMvcTest를 쓰는게 좋을 떄도 있다. @SpringBootTest는 프로젝트의 전체 컨텍스트를 로드하여 빈을 주입하기 때문에 속도가 느리고, 통합 테스트를 할 때 많이 사용한다. 필요한 빈만 등록하여 테스트를 진행하고자 한다면, 슬라이스 테스트 어노테이션인 @WebMvcTest를 사용하는 것이 효율적이다.
슬라이스 테스트란?
특정 부분만 테스트할 수 있는 테스트를 말한다. 아래는 대표적인 슬라이스 테스트 어노테이션이다. @WebMvcTest - Controller를 테스트할 수 있도록 관련 설정을 제공 - 특정 컴포넌트만 Bean으로 등록한다. - 이 밖에 테스트를 하는 데 필요하지 않은 @Service, @Repository 등은 Bean으로 등록하지 않는다. 이 밖에도 WebFluxTest, DataJpaTest, DataJpaTest, JsonTest, RestClientTest 등이 존재한다.
@WebMvcTest는 MVC 부분 슬라이스 테스트로, 보통 컨트롤러 하나만 테스트하고 싶을 때 사용한다. 가끔 NoSuchDefinitionException 오류가 나는데, Service와 같은 일반적인 컴포넌트는 생성되지 않는 특성 때문이다. 해당 컨트롤러를 생성하는 데 필요한 다른 빈을 정의하지 못해 발생한다. 따라서 이런 경우에는 @MockBean 을 사용해서 필요한 의존성을 채워주어야 한다.
MockMvc
MockMvc를 활용한 Controller 슬라이스 테스트를 진행해본다.
GET
@Autowired
private MockMvc mockMvc;
@Autowired
private UserRepository userRepository;
@Autowired
private ObjectMapper objectMapper;
@DisplayName("회원정보 조회")
@Test
public void findUserByEmailTest() throws Exception {
User user = User.builder()
.name("name")
.email("blabla@gmail.com")
.password("pwd")
.role("ROLE_USER")
.build();
Long id = userRepository.save(user);
mockMvc.perform(get("/api/user/") + id)
.andExpect(status().isOk())
.andDo(print());
}
POST
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@DisplayName("회원가입")
@Test
public void signupTest() throws Exception {
Map<String, String> input = new HashMap<>();
input.put("name", "kimdozzi");
input.put("email", "dozzi@gmail.com");
input.put("password", "pwd");
mockMvc.perform(post("/signup")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(input))) //Map으로 만든 input을 json형식의 String으로 만들기 위해 objectMapper를 사용
.andExpect(status().isOk())
.andDo(print());
}
PATCH
@Autowired
private MockMvc mockMvc;
@Autowired
private UserRepository userRepository;
@Autowired
private ObjectMapper objectMapper;
@DisplayName("회원정보 수정")
@Test
public void UpdateUser() throws Exception {
User user = User.builder()
.name("kimdozzi")
.email("dozzi@gmail.com")
.password("pwd")
.role("ROLE_USER")
.build();
Long id = userRepository.save(user);
Map<String, String> updateUser = new HashMap<>();
updateUser.put("name", "updated_dozzi");
updateUser.put("password", "updated_pwd");
mockMvc.perform(patch("/user/" + id)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(input)))
.andExpect(status().isOk())
.andDo(print());
}
DELETE
@Autowired
private MockMvc mockMvc;
@Autowired
private UserRepository userRepository;
@DisplayName("회원정보 삭제")
@Test
public void deleteUserTest() throws Exception {
User user = User.builder()
.name("kimdozzi")
.email("dozzi@gmail.com")
.password("pwd")
.role("ROLE_USER")
.build();
Long id = userRepository.save(user);
mockMvc.perform(delete("/user/" + id))
.andExpect(status().isOk())
.andDo(print());
Assertions.assertThat(userRepository.findByName("kimdozzi")).isEmpty());
}
참고 자료