Polpid's World

[SpringBoot] @Mock, @MockBean 차이가 뭘까? 본문

Development/Java

[SpringBoot] @Mock, @MockBean 차이가 뭘까?

폴피드 2018.04.10 18:47

테스트 케이스를 작성하다가 좀 헷갈리는게 있었다. @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의 ApplicatioinContext에 넣어준다. 그리고 동일한 타입의 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 ontext 가 올라간다. 그래서 부득이하게 @MockBean 으로 UserService 와 EmailService를 넣어줘야 했다. 1번은 Controller 객체에 대해서 Mock 을 만들어서 그거에 대해서만 테스트를 한다.(뭐라고 표현을 해야할지 잘 모르겠다.)


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


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

0 Comments
댓글쓰기 폼