Mockito - @Mock, @Spy, @MockBean, @SpyBean
업데이트:
Java에서 테스트 코드를 작성하고, Test Double을 사용하려 하면 거의 필수적으로 마주치게 되는 것이 Mockito 입니다. 오늘은 이 Mockito에서 제공하는 @Mock
, @Spy
과 이와 비슷한 용도로 Spring에서 제공하는 @MockBean
, @SpyBean
에 대해서 포스팅해보도록 하겠습니다.
Mockito?
먼저 Mockito란 무엇일까요??
Mockito는 Java용 오픈 소스 테스트 프레임워크이며, 테스트를 위한 Test Double을 쉽게 생성할 수 있고, 관련 검증 메소드를 제공해주고 있습니다.
@Mock
원래는 Mockito.mock()
메소드를 통해 Mock 객체로 만들지만 Mockito는 @Mock
어노테이션을 통해 특정 객체를 Mock객체로 바인딩 해주기도 합니다.
@ExtendWith(MockitoExtension.class)
public class BoardServiceTest {
@Mock
private PostRepository postRepository;
@InjectMocks
private PostService postService;
@Test
void mockitoMockTest() {
PostRepository mockPostRepository = mock(PostRepository.class);
}
}
위의 샘플 코드에서 postRepository
, mockPostRepository
를 Mock 객체로 선언했습니다.
디버깅을 콘솔을 보면 둘다 Mock 객체가 되어있는 것을 확인 할 수 있습니다.
@Spy
Mock은 개발자가 지정한 Stub말고 원래의 기능은 동작하지 않습니다. (Mock이기 때문에)
하지만 지정한 Stub을 제외한 나머지 기능을 그대로 사용하고 싶은 경우가 생기게 되는데, 이때 사용할 수 있는 어노테이션이 @Spy
입니다.
@ExtendWith(MockitoExtension.class)
public class BoardServiceTest {
@Spy
private PostRepository postRepository;
@InjectMocks
private PostService postService;
@Test
void mockitoMockTest() {
final Long id = 1L;
given(postRepository.findById(id))
.willReturn(Optional.ofNullable(new Post(Tag.GAME, Category.FREE)));
postRepository.realFindPost();
Optional<Post> stubbingPost = postRepository.findById(id);
}
}
위의 샘플 코드에서 PostRepository
를 Spy로 선언하였고, given()
메소드를 통해 findById()
메소드에 대해 Stubbing 하였습니다. 그리고 테스트 메소드에선 postRepository
의 realFindPost()
메소드와 findById()
메소드를 실행 했습니다. realFindPost()
메소드는 다음과 같습니다.
public interface PostRepository extends JpaRepository<Post, Long> {
default Post realFindPost(){
System.out.println("real PostRepository realFindPost");
return new Post(Tag.SHOPPING, Category.FREE);
}
}
만약 Spy로 선언해서 진짜 realFindPost()
가 실행된다면 real PostRepository realFindPost
가 콘솔에 출력되겠죠??
테스트를 돌려보니 정상적으로 출력되는 것을 확인할 수 있습니다.
그리고 Stubbing한 객체로 정상적으로 반환되는 것을 확인할 수 있습니다.
@InjectMocks
@InjectMocks
는 사용하면 위에서 사용한 @Mock
, @Spy
로 지정된 Mock 객체들 중 필요한 객체들을 주입시켜 줍니다. (Spring의 DI와 유사하다고 보면 됩니다.)
MockitoExtension
위의 샘플 코드에서 맨위를 보시면 @ExtendWith(MockitoExtension.class)
부분이 있는데, 테스트 코드를 작성할 때 위의 다양한 어노테이션(@Mock
, @Spy
, @InjectMocks
)을 사용하기 위해선 MockitoExtension
를 추가해줘야 합니다.
💡
SpringExtension
를 추가해줘도 됩니다. Spring안에 있는MockitoTestExecutionListener
를 통해 Mockito의 어노테이션, 메소드를 실행해줌을 확인할 수 있습니다.
Spring의 Test 어노테이션
Mockito에서 제공하는 @Mock
, @Spy
처럼 Spring에서도 Mocking을 위한 다양한 어노테이션을 제공합니다.
@MockBean
Mockito의 @Mock
과 비슷하게 동작합니다.
하지만 다른 점이라면 Spring Context에 Mocking된 Bean에 등록되게 됩니다. 사용하기 위해선 @InjectMocks
을 사용하는 것이 아닌 @Autowired
를 사용하면 됩니다.
@SpringBootTest
public class BoardServiceTest {
@Autowired
ApplicationContext applicationContext;
@MockBean
private PostRepository postRepository;
@Autowired
private PostService postService;
@Test
void mockitoMockTest() {
PostRepository mockBeanPostRepository
= (PostRepository) applicationContext.getBean("postRepository");
}
}
샘플 코드를 보시면 @ExtendWith
를 사용하지 않고, @SpringBootTest
어노테이션을 사용하여 Spring Context를 올렸고, Bean이 올라가도록 했습니다.
그리고 applicationContext.getBean()
를 사용하여 postRepository Bean
이 있는지 확인해보겠습니다.
결과를 보시면 PostRepository
가 Bean으로 올라가 있고, Mock 객체로 되어있음을 알 수 있습니다.
@SpyBean
Mockito의 @Spy
와 비슷하게 동작합니다.
@MockBean
과 마찬가지로 Spring Context에 등록되는 방식으로 동작합니다.
@Autowired
당연한 얘기겠지만 관리하는 영역이 다르기 때문에 @MockBean
사용시에는 @InjectMocks
를 사용할 수 없습니다.
위의 샘플 코드에서 @Autowired
사용시에는 아래와 같이 정상적으로 MockBean을 가져와서 사용합니다.
하지만 @InjectMocks
를 사용 시 아래와 같이 null
로 출력되는 것을 확인할 수 있습니다.
마무리
오늘은 다양한 Test Double 사용 법에 대해 확인해 보였는데요, 개인적으로 단위 테스트를 주로 사용하는 이상 @MockBean
이나 @SpyBean
은 사용할 일이 많이 없을 것 같습니다.
테스트 코드를 작성하시는데 제 글이 도움이 되셨으면 좋겠습니다.
오늘도 제 긴글을 봐주셔서 감사합니다.😀
댓글남기기