테스트를 수행하다보면, 실제 객체를 대신해주는 테스트용 객체를 생성해 줄 때, mock을 사용하게 됩니다. 이때마다 사용했던 mock() 과 @Mock 그리고 @MockBean 세가지에 대해 정리해보겠습니다.
Mockito.mock()
Mockito.mock() 메서드를 사용하면 클래스 또는 인터페이스의 mock 객체를 생성할 수 있습니다.
또한 mock 객체를 사용해, 해당 객체가 가진 메서드의 반환값을 조작하거나 해당 메서드가 호출되었는지 확인할 수 있습니다.
@Test
public void UserRepository를mocking하고_count메서드가실행되면_모킹한값을리턴한다() {
UserRepository localMockRepository = Mockito.mock(UserRepository.class);
Mockito.when(localMockRepository.count()).thenReturn(111L);
long userCount = localMockRepository.count();
Assert.assertEquals(111L, userCount);
Mockito.verify(localMockRepository).count();
}
이렇게 mock() 메서드를 사용하여 mocking된 객체와, 해당 객체의 메서드를 조작해 원하는 값으로 리턴값을 조작할 수 있습니다.
@Mock 애노테이션
@Mock 어노테이션은 앞서 설명한, Mockito.mock() 메서드의 축약어라고 볼 수 있습니다.
- 이 애노테이션은 Mockito.mock()메서드와 달리 테스트 클래스에서만 사용해야 합니다.
- 또한 Mockito annotation을 활성화해야만, @Mock 어노테이션을 사용할 수 있습니다.
@Mock 어노테이션을 활성화 하는 방법으로는 두가지가 있습니다.
- MockitoJunitRunner를 사용해 활성화한다(Junit4에서의 방법)
- Junit5에서는@ExtendWith(MockitoExtension.class) 메서드로 활성화 할 수 있습니다.
@ExtendWith(MockitoExtension.class)
public class MockAnnotationUnitTest {
@Mock
UserRepository mockRepository;
@Test
public void givenCountMethodMocked_WhenCountInvoked_ThenMockValueReturned() {
Mockito.when(mockRepository.count()).thenReturn(123L);
long userCount = mockRepository.count();
Assert.assertEquals(123L, userCount);
Mockito.verify(mockRepository).count();
}
}
이렇게 mocking을 하게 되면, 테스트 코드의 가동성을 높일 수 있고, @Mock 을 사용하게 되면, 테스트가 실패 했을 때 실패 메시지에 세부 내용이 표시되므로 디버깅에 용이합니다.
Wanted but not invoked:
mockRepository.count();
-> at org.baeldung.MockAnnotationTest.testMockAnnotation(MockAnnotationTest.java:22)
Actually, there were zero interactions with this mock.
at org.baeldung.MockAnnotationTest.testMockAnnotation(MockAnnotationTest.java:22)
또한 mocking을 설정할 때, @InjectMocks 를 사용하게 되면, mocking된 객체들을 해당 애노테이션이 설정된 객체에 주입해주기에, mock 객체 주입과 관련된 설정 코드의 양을 줄일 수 있습니다.
- MockitoAnnotations.initMocks() 메서드를 명시적으로 호출해, 활성화한다
@MockBean 애노테이션
스프링 애플리케이션 컨텍스트에 Mock 객체를 추가하기 위해 @MockBean 을 사용할 수 있습니다.
해당 애노테이션으로 mocking 된 객체는 스프링 애플리케이션 컨텍스트에서 동일한 유형을 가진 기존 빈을 대체합니다.
만약 동일한 유형의 빈이 정의되어 있지 않으면 새로운 Bean이 추가됩니다.
이 애노테이션은 외부 서비스와 같은 특정 빈을 모킹해야 하는 통합 테스트 작성시에 유용합니다.
@MockBean 애노테이션을 사용하려면 SpringRunner를 사용하여 테스트를 실행해야 합니다.(Junit4기준)
- Junit5에서는 @ExtendWith(SpringExtension.class) 를 사용하면 됩니다
@RunWith(SpringRunner.class)
public class MockBeanAnnotationIntegrationTest {
@MockBean
UserRepository mockRepository;
@Autowired
ApplicationContext context;
@Test
public void givenCountMethodMocked_WhenCountInvoked_ThenMockValueReturned() {
Mockito.when(mockRepository.count()).thenReturn(123L);
UserRepository userRepoFromContext = context.getBean(UserRepository.class);
long userCount = userRepoFromContext.count();
Assert.assertEquals(123L, userCount);
Mockito.verify(mockRepository).count();
}
}
위 코드와 그림처럼 UserRepository 필드에 애노테이션을 사용하게 되면, mocking된 객체가 해당 필드에 주입되고 애플리케이션 컨텍스트에 등록되게 됩니다.
결론
mock() 메서드나, @Mock 를 사용하게 되면, 스프링 애플리케이션 컨텍스트를 로딩하지 않고도 사용 할 수 있기에, 단위 테스트 시에 유용하게 사용 될 수 있을 겁니다. 또한 단위 테스트 시에도 무분별하게 mocking을 통해 테스트를 작성하기 보다는 위 글에서 설명하는 것 처럼 외부 라이브러리와 같이직접 제어를 할 수 없는 경우에 mocking을 사용하면 좋을 것 입니다.
반면 @MockBean 메서드는 스프링 애플리케이션 컨텍스트를 로딩해야 사용할 수 있기 때문에, 스프링 애플리케이션 컨텍스트를 필요로 하는 통합 테스트에서 mocking이 필요할 때 유용하게 사용될 수 있을 것 입니다.
이렇듯 각 애노테이션이나 메서드의 특징에 대해 알고 사용해야 올바르게 사용할 수 있는 가장 빠른 길이라고 생각해 글을 작성하게 됐습니다.
'프로그래밍 > Spring' 카테고리의 다른 글
Spring Data Common 모듈 에서의 디자인 패턴 (2) | 2023.05.27 |
---|---|
Micro Service에서 WebFlux의 장점 (0) | 2022.12.28 |
Spring WebMVC VS WebFlux (0) | 2022.12.28 |