참고
https://gocheat.github.io/spring/spring_test-1/
https://ktko.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81Spring-MockMvc-%ED%85%8C%EC%8A%A4%ED%8A%B8
https://we1cometomeanings.tistory.com/65
실무에서 단위테스트로 간단한 진행만 하지 TDD로 개발은 하지 않고 있는데 개발하면 할 수록 중요성이 너무 느껴서 열심히 사용해 보려고 한다.
@ExtendWith(MockitoExtension.class)
: Mockito의 Mock 객체를 사용
@Mock
: Mock 객체
@InjectMocks
: @Mock 객체를 주입할 주체 객체
public class AuthController {
@PostMapping("/save")
public ApiResponse save(@RequestBody User user) {
if (userService.getUser(user.getUserName()) != null) {
return ApiResponse.fail();
}
userService.save(user);
return ApiResponse.success(HttpStatus.OK.name(), null);
}
}
위의 코드를 테스트 하려고 할 때면
@Mock
어노테이션으로 UserService
를 Mock 객체로 만들어주고
@InjectMocks
어노테이션으로 AuthController
를 객체를 주입 할 주제 객체로 선언해주면 될 줄 알았으나 생각해보니 userService를 Test 코드 자체에서 사용하는 것이 아니라 Controller 안에서 사용하니까 다른 방법이 필요해 보였다.
그 다른 방법으로 사용하는 것이 MockMvc 방법이다.
웹 애플리케이션을 애플리케이션 서버에 배포하지 않고 스프링의 MVC 동작을 테스트 할 수 있는 클래스이다.
사실 처음에는 비즈니스 로직을 구현하는 서비스단을 테스트하면 컨트롤러 단을 테스트 할 필요가 있을까? 싶었지만 일반적인 단위 테스트 형태가 아니라 통합 테스트 관점으로 생각하면 의미가 있다고 한다.
웹 환경에서 컨트롤러를 테스트하기 위해서는 서블릿 컨테이너 구동, DispatcherServlet 객체가 메모리에 올라갸아함. 서블릿 컨테이너를 모킹하면 실제가 아니라 테스트용 모형 컨테이너를 사용하면 간단하게 컨트롤러를 테스트 할 수 있음. --> 이 부분을 위해서 @WebMvcTest
또는 @AutoConfigureMockMvc
를 사용한다.
완성된 테스트 코드
package com.studygram;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.studygram.common.oauth.ProviderType;
import com.studygram.common.oauth.RoleType;
import com.studygram.controller.AuthController;
import com.studygram.domain.User;
import com.studygram.service.UserService;
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
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.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class AuthControllerTest {
@Mock
private User user;
@Autowired
private MockMvc mockMvc;
AutoCloseable openMocks;
ObjectMapper objectMapper = new ObjectMapper();
@BeforeEach
public void setup() {
openMocks = MockitoAnnotations.openMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(AuthController.class).build();
}
@Test
@DisplayName("로컬 회원 가입 테스트")
public void saveTest() throws Exception
{
// given
user = User.builder()
.userName("minchoi")
.passwd("1234")
.providerType(ProviderType.LOCAL)
.roleType(RoleType.USER)
.build();
// when & then
mockMvc.perform(MockMvcRequestBuilders
.post("/api/v1/auth/save")
.content(objectMapper.writeValueAsString(user))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
우선 @AutoConfigureMockMvc
와 @WebMvcTest
중에 고민을 했는데 친구가 회사에서 사용한다는 테스트 코드 훔쳐보니까 @AutoConfigureMockMvc
을 사용하고 있어서 일단 사용해봤다. 차이점은 더 알아보자.
파라미터 설정
params vs content
get 으로 보낼 때는 params로 붙여서 보내고
body에 집어넣어서 보낼 때는 content로 붙여서 json으로 보낸다.
@WebMvcTest
@Controller
, @RestController
가 설정된 클래스를 찾아 메모리에 생성한다. @ActiveProfiles