반응형

2021/02/17 - [Development/Java] - Spring Boot Test Case 작성에 대한 생각 - Service Test

2021/02/17 - [Development/Java] - Spring Boot Test Case 작성에 대한 생각 - Repository Test

Repository, Service 에 대한 테스트를 살펴봤으니 이제 Controller 테스트를 확인해보자. Controller Test 에는 @WebMvcTest 를 사용했다. Controller 는 확인해야 할 부분이 다음과 같다. 

1. request 를 요청한 url 과 파라메터가 정확한지 여부.
2. 정상 처리 되었을데 요구한 응답을 보내주는지.
3. 비정상일때에 response 에 상태 코드가 정확히 전달 되는지. 

@RunWith(SpringRunner.class)
@WebMvcTest(UserRestController.class)
public class UserRestControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    public void getUsers() throws Exception {

        when(userService.findAll())
                .thenReturn(Arrays.asList(
                        Users.builder()
                                .email("test@test.com")
                                .name("test")
                                .status(UserStatus.APPLIED)
                                .build()
                ));

        this.mockMvc.perform(get("/users"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("@[0].email").value("test@test.com"));

    }
}

테스트케이스를 통해서 response 로 받는 내용과 상태 코드, 그리고 body 에 있는 내용들을 확인 해 볼 수 있다. 

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = [{"email":"test@test.com","name":"test","userStatus":"APPLIED"}]
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

그리고 print() 를 했기 때문에 콘솔 창에 위와 같은 결과도 같이 확인 해 볼 수 있다. 

그리고 잘못된 결과값이 나올 경우에도 response 가 잘 리턴 되는지 확인해 봐야 한다.

@Test
public void getUserNotFoundException() throws Exception {

  when(userService.findUserById("test11@test.com"))
    .thenThrow(
      new RuntimeException("User not found")
  );

  this.mockMvc.perform(get("/users/test11@test.com"))
    .andDo(print())
    .andExpect(status().isBadRequest());
}


위 테스트는 파라메터로 "test11@test.com" 을 보냈으나 사용자가 존재하지 않을때 400 error 와 메세지를 보내는 경우이다. 

MockHttpServletResponse:
           Status = 400
    Error message = null
          Headers = [Content-Type:"text/plain;charset=UTF-8", Content-Length:"14"]
     Content type = text/plain;charset=UTF-8
             Body = User not found
    Forwarded URL = null
   Redirected URL = null

결과로 400 에러가 전달 되며 Body 에는 메세지가 써있다. 

Controller 테스트에서는 고려해야 할 것들이 많은것 같다. request 를 받고 response 를 보내야 하는 곳이기 때문에 파라메터에 대한 검증과 상태코드, response 값과 message 들이 잘 구성되어있어야 한다. Status 코드가 200이 떨어졌을때 보다는 200이 아닌경우의 상황을 더 많이 고려해야 되지 않을까 생각이 된다. 

주의사항) 제 생각을 기준으로 작성하고 만든 소스코드이고 의견이기 때문에 틀린 부분이 있을수 있습니다.

728x90
반응형
반응형

테스트 케이스를 작성하다가 좀 헷갈리는게 있었다. @Mock, @MockBean 차이가 뭐지??? 쓰긴 하고 있는데 알고 써야 하지 않을까라는 의문이 들었다. 그래서 찾아봤다.

 

먼저 Mock 객체를 선언할 때에는 2가지 방법이 있다.

 

1. 첫번째 : mock() 을 이용해서 선언

 

1
2
3
4
5
6
7
8
9
10
11
12
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
 
    @InjectMocks
    private UserService userService;
    UserRepository userRepository = mock(UserRepository.class);
 
    @Test
    public void findByEmail_test(){
        when(userRepository.findByEmail("test@test.com")).thenReturn(Optional.of(new User()));
        userService.findByEmail("test@test.com");
        verify(userRepository).findByEmail("test@test.com");
    }
}
cs

 

2. 두번째 : @Mock 을 이용해서 선언

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
 
    @InjectMocks
    private UserService userService;
 
    @Mock
    private UserRepository userRepository;
 
    @Test
    public void findByEmail_test(){
        when(userRepository.findByEmail("test@test.com")).thenReturn(Optional.of(new User()));
        userService.findByEmail("test@test.com");
 
        verify(userRepository).findByEmail("test@test.com");
    }
}
cs

 

이 2개의 테스트 케이스는 동일하게 동작한다. 

 

그리고 선언한 Mock 객체를 테스트를 실행하는 동안 사용할수 있게 하기 위한 작업이 있다. 바로 위에서 보이는 @RunWith(MockitoJUnitRunner.class) 가 바로 그 역할을 해준다.  또다른 방법으로는 아래와 같은 메소드를 테스트케이스 실행 전에 실행 시키면 된다. 

 

 

1
2
3
4
@Before 
public void initMocks() {
    MockitoAnnotations.initMocks(this);
}
cs

 

여태껏 아무생각없이 둘다 @RunWith랑 initMocks랑 둘다 썼는데 둘다 쓸 필요가 없었다. ㅡㅡ;

 

그럼 @MockBean 은 뭐지??

 

@MockBean 은 Mock 과는 달리 org.springframework.boot.test.mock.mockito 패키지 하위에 존재한다. 즉 spring-boot-test에서 제공하는 어노테이션이다. Mockito 의 Mock 객체들을 Spring의 ApplicationContext에 넣어준다. 그리고 동일한 타입의 Bean  이 존재할 경우 MockBean으로 교체해준다.

 

그럼 언제 @Mock을 쓰고 언제 @MockBean 을 써야 하나??

 

결론적으로 Spring Boot Container가 필요하고 Bean이 container 에 존재 해야 한다면 @MockBean 을 쓰면 되고 아니라면 @Mock 을 쓰면 된다. 그런데 개인적으로는 그걸 잘 판단을 못하겠다.

 

1. MockitoJUnitRunner 를 이용해서 Mock을 사용한 Testcase.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RunWith(MockitoJUnitRunner.class)
public class RegisterControllerMockTest {
 
    @InjectMocks
    private RegisterController registerController;
 
    private ModelAndView model;
 
    @Before
    public void setup(){
        model = new ModelAndView();
    }
 
    @Test
    public void showRegistrationPage_test() throws Exception {
 
        registerController.showRegistrationPage(model, new User());
        Assert.assertEquals("register", model.getViewName());
    }
}
 
cs

 

2. SpringRunner와 @WebMvcTest를 사용해서 @MockBean 을 사용한 Testcase

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@RunWith(SpringRunner.class)
@WebMvcTest(value = RegisterController.class, secure = false)
public class RegisterControllerTest {
 
    @Autowired
    private MockMvc mockMvc;
 
    @MockBean
    private UserService userService;
 
    @MockBean
    private EmailService emailService;
 
    @Test
    public void showRegistrationPage_test() throws Exception {
 
        mockMvc.perform(MockMvcRequestBuilders.get("/register"))
                .andExpect(status().isOk())
                .andExpect(view().name("register"))
                .andDo(print());
    }
}
cs

분명 이 2개의 테스트 케이스 는 결과는 같지만 실제 내부에서 동작하는 부분은 다르다. 2번만 spring applicaiton context 가 올라간다. 그래서 부득이하게 @MockBean 으로 UserService 와 EmailService를 넣어줘야 했다. 1번은 Controller 객체에 대해서 Mock 을 만들어서 그거에 대해서만 테스트를 한다.(뭐라고 표현을 해야할지 잘 모르겠다.)

 

이렇게 같은 컨트롤러에 대한 테스트 이지만 작성하기에 따라서 여러가지 방법으로 테스트 케이스를 만들 수 있다. 아직은 미숙하지만 좀더 많이 작성하다 보면 방법도 많이 알게 되고 실제 구현 코드들의 모습도 많이 나아질거라는 생각이 든다.

 

참고 : https://stackoverflow.com/questions/44200720/difference-between-mock-mockbean-and-mockito-mock

728x90
반응형

+ Recent posts