ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • MockTest 간단 사용법 (RestDocs 를 위한)
    SPRING/Spring Boot 2022. 6. 28. 01:36

    <피드백 언제나 환영합니다! 댓글 감사합니다>

    Spring swagger 3.0.0 설정 에서 사용한 repository를 그대로 따라갈 예정 입니다. 기본적인 RestDocs 사용법 이 끝난 이후는 Test 부분을 다뤄 볼 예정 입니다.

    Mock 이란?

    • 실제 객체를 만들어 사용하기에 시간, 비용등이 높거나 객체 서로간의 의존성이 강해 힘들경우 가짜 객체를 만들어 사용하는 방법

    실제 Spring Web 을 이용한 Controller 부분 이나 Service 부분이 이해 해당됩니다 조금더 구체적으로 말하면 네트워크 데이터베이스등, 단위 테스트가 어려울때 즉, 시스템의 다른 부분에 많이 얽혀 있고 의존해 있다면 고려해볼만 합니다. 사용해야 합니다.

    라이브러리 추가

    //build.gradle
    dependencies {
    	  ....
        testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
    		....
    }

    Mock을 통해 유닛 테스트하기

    테스트를 하기전 기존의 코드에 간단하게 추가해야할 부분이 있어 추가 할 예정 입니다.

    • TestController.java
      ....
      @RestController
      public class TestController {
      
          @Autowired
          private TestUserService userService;
      
          @PostMapping(value = "/user")
          public UserDto helloUser(@RequestBody UserDto userDto){
              return userService.createUserDto(userDto);
          }
      
      		@GetMapping(value = "/userInformation/{id}")
          public UserDto userInformation(@PathVariable Integer id){
              return userService.findUserDto(id);
          }
      	....
      }

    UserDto 를 이용하여 User 를 등록하는 Controller를 추가 해 줍니다. → Controller 를 통하여 등록 과 조회를 두부분을 나누어 이야기 해볼 생각입니다.

    • TestUserService.java
      @Service
      public class TestUserService {
          private static final Map<Integer, UserDto> USER_DTO_MAP = new HashMap<>();
      
          public UserDto createUserDto(UserDto userDto) {
              if(USER_DTO_MAP.containsKey(userDto.getId())){
                  throw new IllegalArgumentException("중복 Id");
              }
              USER_DTO_MAP.put(userDto.getId(), userDto);
              return userDto;
          }
      
          public UserDto findUserDto(Integer id) {
              return USER_DTO_MAP.get(id);
          }
      }

    DB 연동의 경우 설정 부분이 많아 간소화 하여, Map 형태로 관리하는 형식으로 지정 해둡니다.

    위의 두 코드가 추가가 되었다면 test 모듈에서 TestController 에 대한 Test 를 만들어 줍니다.

    • TestControllerTest.java
      //@SpringBootTest
      @WebMvcTest(TestController.class)
      @AutoConfigureRestDocs
      class TestControllerTest {
      
          @Autowired
          private MockMvc mockMvc;
      
          @Autowired
          private ObjectMapper objectMapper;
      
          @MockBean
          private TestUserService userService;
      
          private UserDto getUserDto(String hello_MockTest) {
              UserDto userDto = new UserDto();
              userDto.setStatus(true);
              userDto.setId(1);
              userDto.setCreateAt(LocalDateTime.now());
              userDto.setName(hello_MockTest);
              return userDto;
          }
      
          @Test
          public void mockMvcTestCreate() throws Exception {
              UserDto userDto = getUserDto("MockTest");
      
              given(userService.createUserDto(userDto)).willReturn(userDto);
      
              String content = objectMapper.writeValueAsString(userDto);
      
              ResultActions perform = mockMvc.perform(post("/user")
                      .content(content)
                      .contentType(MediaType.APPLICATION_JSON)
                      .accept(MediaType.APPLICATION_JSON));
      
              perform.andExpect(status().isOk())
                      .andExpect(jsonPath("$.id").exists())
                      .andExpect(jsonPath("$.name").value(userDto.getName()))
                      .andDo(print());
          }
      
          @Test
          public void mockMvcTestFind() throws Exception {
              UserDto userDto = getUserDto("MockTest");
      
              given(userService.createUserDto(userDto)).willReturn(userDto);
              when(userService.findUserDto(userDto.getId())).thenReturn(userDto);
      
              ResultActions perform = mockMvc.perform(get("/userInformation/{id}", 1)
                      .contentType(MediaType.APPLICATION_JSON)
                      .accept(MediaType.APPLICATION_JSON));
      
              perform.andExpect(status().isOk())
                      .andExpect(jsonPath("$.id").exists())
                      .andExpect(jsonPath("$.name").value(userDto.getName()))
                      .andDo(print());
          }
    • //@SpringBootTest → MockTest를 할때 Spring 을 사용해야해서 켜놓을거라 생각하지만 실제 IllegalStateException: Configuration error:found multiple declarations of @BootstrapWith 에러를 발생 시킵니다.
    • @WebMvcTest(TestController.class) → MockTest 시 반드시 써야 하는 부분이며 해당 Controller 또는 Service를 적어 사용 하면 됩니다. @SpringBootTest 와 함께 쓰면 안되는데 이유는 이 어노테이션 안에도 @BootstrapWith 가 내장되어 있어 중복 설정 오류를 발생 시킵니다.
    • @AutoConfigureRestDocs → 후 Rest docs 로 Swagger와 연계하기위해 만들어둔 어노테이션 입니다. (후에 RestDocs 에 다루겠습니다.)

    Controller 에서 새로 추가된 두 method 만 테스트를 진행 하였으며 간략히 살펴보겠습니다.

    (MockTest 의 경우 static으로 추가된 부분이 많습니다. 어떤 부분을 사용하였는지 적어 놓을 예정입니다.)

    @Test
        public void mockMvcTestCreate() throws Exception {
            UserDto userDto = getUserDto("MockTest");
    
            given(userService.createUserDto(userDto)).willReturn(userDto);
    
            String content = objectMapper.writeValueAsString(userDto);
    
            ResultActions perform = mockMvc.perform(post("/user")
                    .content(content)
                    .contentType(MediaType.APPLICATION_JSON)
                    .accept(MediaType.APPLICATION_JSON));
    
            perform.andExpect(status().isOk())
                    .andExpect(jsonPath("$.id").exists())
                    .andExpect(jsonPath("$.name").value(userDto.getName()))
                    .andDo(print());
        }
    • given() → import static org.mockito.BDDMockito.given;
      • mock 에서 가로채 실제 url 이 호출될때 작업해야하는걸 잡아 둡니다.
      • mock 객체의 동작을 미리 정의 하여 리턴되는 값을 미리 생성 합니다
    • perform() → Rest 를 정의하고 들어가는 url을 정의합니다.
    • post() → org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;MockMvcRequestBuilders 로 간단히 표시
      • Rest 중 post 로 정의하고 url 을 정의합니다.
    • status() → MockMvcRequestBuilders
      • HTTP의 상태 코드 값을 정의 합니다.
    • jsonPath() → MockMvcRequestBuilders
      • 결과 값이 json 형태로 왔을시 $ 를 이용하여 json 값을 표현할 수 있습니다.
      • $.{…} → {…} depthLevel 을 뜻하며 json 값들을 호출하여 불러올 수 있습니다.
    @Test
        public void mockMvcTestFind() throws Exception {
            UserDto userDto = getUserDto("MockTest");
    
            given(userService.createUserDto(userDto)).willReturn(userDto);
            when(userService.findUserDto(userDto.getId())).thenReturn(userDto);
    
            ResultActions perform = mockMvc.perform(get("/userInformation/{id}", 1)
                    .contentType(MediaType.APPLICATION_JSON)
                    .accept(MediaType.APPLICATION_JSON));
    
            perform.andExpect(status().isOk())
                    .andExpect(jsonPath("$.id").exists())
                    .andExpect(jsonPath("$.name").value(userDto.getName()))
                    .andDo(print());
        }
    • when() → MockMvcRequestBuilders
      • mock 에서 when 안에 동작을 할때 return 으로 어떤 값을 내보내는지 정의 하기 위함입니다.
      • 실제 mock 에서 url 이 호출 될때 when 동작을 통해 값을 확인할때 사용 합니다.
    • get() → MockMvcRequestBuilders
      • Rest 중 get으로 정의하고 url 을 정의합니다.

    github 코드 : https://github.com/Eco-Min/Swagger

    'SPRING > Spring Boot' 카테고리의 다른 글

    Spring swagger 3.0.0 설정  (1) 2022.06.22
    LogBack - 설정(.xml)  (0) 2022.03.07
    Profile.active 설정 정보 (작성중..)  (0) 2022.03.02

    댓글

Designed by Tistory.