@Mock은 Mockito에서 제공하는 어노테이션으로, 테스트 코드에서 의존성을 갖는 객체의 가짜(Mock) 객체를 만들어주는 데 사용됩니다. 실제 객체 대신 Mock 객체를 생성하고, 이 객체는 실제 로직을 실행하는 대신 미리 정의된 동작을 수행합니다. 이를 통해 외부 의존성을 제거하고 테스트 대상 코드에만 집중할 수 있습니다.
null, 0, false 등의 기본 값을 반환합니다.@Mock은 인터페이스 또는 클래스의 가짜(Mock) 객체를 만듭니다. 이 객체는 실제 구현체와는 다르며, 메서드를 호출했을 때 기본적으로 아무 동작도 하지 않고 기본값(null, 0, false 등)을 반환합니다.@Mock은 객체의 구현체 없이도 사용할 수 있다는 것입니다. 인터페이스를 Mocking 할 수 있으며, 인터페이스의 메서드는 기본적으로 null을 반환합니다.@Mock
private MyService myService; // 인터페이스도 가능
@Test
void testMock() {
// getData() 호출 시 아무 동작도 하지 않고 null 반환
assertNull(myService.getData());
}
null, 0, false 등 Java의 기본값이 반환됩니다.@Mock 사용 예시 코드
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
class ServiceTest {
@Mock
private ExternalService externalService; // 가짜 객체 생성
@Test
void testService() {
// Mockito로 Mock 객체 생성
externalService = Mockito.mock(ExternalService.class);
// getData() 메서드가 호출되면 "Mocked Data"를 반환하도록 설정
when(externalService.getData()).thenReturn("Mocked Data");
// 테스트 대상 서비스에 Mock 객체 주입
MyService myService = new MyService(externalService);
// Mock 동작 검증
assertEquals("Mocked Data", myService.getDataFromService());
}
}
@Mock 사용: ExternalService의 가짜 객체를 생성하여 실제 서비스 호출 없이 테스트할 수 있습니다.Mockito.when(): 특정 메서드 호출 시 반환할 값을 미리 지정합니다.assertEquals(): Mock 객체로 설정된 값이 올바르게 반환되는지 검증합니다.@Spy는 실제 객체를 기반으로 부분적인 Mocking을 할 수 있는 어노테이션입니다. 즉, 스파이 객체는 실제 객체처럼 동작하되, 일부 메서드만 가짜 동작을 수행하도록 설정할 수 있습니다. 이 방식은 실제 객체의 로직을 대부분 유지하면서 필요한 부분만 Mocking할 수 있어 더 유연한 테스트를 지원합니다.
@Spy는 구현체를 기반으로 동작합니다. 즉, @Spy는 실제 객체를 감시하는 것이기 때문에, 반드시 구현된 클래스가 필요합니다. 인터페이스가 아닌, 구체적인 구현체를 사용해야 합니다.@Spy는 실제 객체의 동작을 대부분 유지하면서, 필요한 경우 일부 메서드를 Mocking하여 동작을 정의할 수 있습니다. 그렇기 때문에 실제 객체가 존재해야만 동작할 수 있습니다.@Spy
private MyServiceImpl myService = new MyServiceImpl(); // 구현체 필요
@Test
void testSpy() {
// 실제 메서드는 원래 동작을 수행
assertEquals("Real Data", myService.getData());
// 특정 메서드만 가짜로 설정 가능
doReturn("Spied Data").when(myService).getData();
assertEquals("Spied Data", myService.getData());
}
| 특징 | @Mock | @Spy |
|---|---|---|
| 객체 생성 | 가짜(Mock) 객체를 생성하여 모든 메서드가 기본값을 반환 | 실제 객체(구현체)를 기반으로 동작, 일부 메서드만 가짜로 설정 |
| 인터페이스 사용 가능 여부 | 인터페이스와 클래스를 모두 Mocking 가능 | 구현체만 사용 가능 (인터페이스 불가) |
| 기본 동작 | 모든 메서드는 아무 동작도 하지 않으며 기본값 반환 | 실제 메서드는 원래 동작을 수행하며, 필요한 경우만 Mocking 가능 |
| Mocking 대상 | 모든 메서드가 Mocking 처리됨 | 특정 메서드만 Mocking하고 나머지는 실제 동작 |
@Mock은 인터페이스든 클래스든 가짜 객체를 만들 수 있으며, 모든 메서드는 기본적으로 아무 동작도 하지 않거나 기본값을 반환합니다. 그래서 외부 의존성을 제거한 상태에서 완전히 가짜 객체로 테스트를 진행할 수 있습니다.@Spy는 실제 구현체가 필요하며, 구현된 클래스의 인스턴스를 사용해야 합니다. 실제 객체를 사용하면서도 특정 메서드만 Mocking할 수 있습니다. 기본적으로는 객체의 모든 메서드가 실제 동작을 수행합니다.보통 @Mock과 @Spy 어노테이션은 서비스 레이어(Service Layer)에서 테스트를 진행할 때 많이 사용됩니다. 그 이유는 서비스 레이어가 비즈니스 로직을 처리하는 핵심 부분이며, 이 로직이 주로 외부 의존성(예: 데이터베이스, 외부 API, 리포지토리, 다른 서비스)을 가지고 있기 때문입니다. 이 의존성들을 Mocking 또는 Spying하여, 테스트 대상이 되는 핵심 비즈니스 로직만 집중해서 테스트할 수 있습니다.
서비스 레이어의 메서드들이 리포지토리나 외부 API와 같은 의존성에 의존하여 데이터를 가져오거나 조작하는 경우가 많습니다. 이런 외부 의존성을 Mock 처리함으로써 실제 호출을 하지 않고, 가짜 동작을 정의하여 비즈니스 로직을 검증할 수 있습니다.
@Mock을 서비스 레이어에서 사용한 예시 코드
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(MockitoExtension.class) // MockitoExtension을 통해 Mockito 기능 활성화
class UserServiceTest {
@Mock
private UserRepository userRepository; // 외부 의존성 Mocking
@InjectMocks
private UserService userService; // 실제 테스트할 서비스 객체
@Test
void testFindUserById() {
// 가짜 동작 정의
when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "John")));
// 실제 서비스 메서드 호출
User result = userService.findUserById(1L);
// 결과 검증
assertEquals("John", result.getName());
// userRepository의 findById() 메서드가 정확히 한 번 호출되었는지 검증
verify(userRepository, times(1)).findById(1L);
}
}
@ExtendWith(MockitoExtension.class)는 JUnit 5에서 Mockito 기능을 활성화하는 어노테이션입니다. 이를 통해 @Mock 및 @InjectMocks 어노테이션이 정상적으로 동작하게 됩니다.@Mock으로 선언된 객체들은 MockitoExtension이 실행되면서 자동으로 Mock 객체로 초기화됩니다. @InjectMocks도 의존성 주입을 통해 자동으로 실제 객체에 Mock 객체를 주입해줍니다.@Mock: UserRepository는 외부 의존성이므로, 실제 데이터베이스를 호출하지 않고 가짜 데이터를 반환하도록 설정했습니다.@InjectMocks: 테스트하려는 서비스인 UserService에 Mock된 리포지토리를 주입합니다.@Spy는 실제 객체의 일부 동작을 유지하면서, 특정 메서드만 Mocking해야 할 때 유용합니다. 예를 들어, 서비스 내 일부 메서드의 로직은 실제로 테스트하면서, 다른 부분은 Mocking하여 원하는 대로 제어할 수 있습니다.
@Spy를 서비스 레이어에서 사용한 예시 코드
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
class OrderServiceTest {
@Spy
private OrderService orderService = new OrderService(); // 실제 구현체를 사용
@Mock
private PaymentService paymentService; // 외부 의존성 Mocking
@InjectMocks
private OrderProcessingService orderProcessingService; // 실제 테스트할 서비스
@Test
void testProcessOrder() {
// 특정 메서드만 가짜 동작을 설정
doReturn(true).when(orderService).checkInventory(anyLong());
// 결제 서비스 Mock 동작 설정
when(paymentService.processPayment(any())).thenReturn(true);
// 실제 서비스 메서드 호출
boolean result = orderProcessingService.processOrder(123L);
// 결과 검증
assertTrue(result);
// checkInventory 메서드가 호출되었는지 검증
verify(orderService).checkInventory(123L);
}
}
@Spy: OrderService는 실제 객체로 생성되며, 이 객체의 모든 메서드는 기본적으로 실제 동작을 수행합니다. 하지만 checkInventory 메서드만 가짜 동작으로 설정했습니다.@Mock: PaymentService는 외부 의존성이므로 완전히 가짜 객체로 만들어 Mocking 처리했습니다.@Mock과 @InjectMocks 조합은 외부 의존성을 제거한 상태에서 서비스 로직을 검증하는 것이 주된 목적입니다.
@InjectMocks는 서비스 객체의 실제 메서드를 호출하지만, 의존성을 주입할 때 @Mock으로 설정된 가짜 객체를 주입합니다.Repository)의 호출을 가짜로 설정하여 데이터베이스나 외부 시스템에 영향을 주지 않고 독립적으로 테스트할 수 있게 됩니다.@Mock은 주로 의존성을 대체하는 용도로 사용됩니다.@Spy는 실제 객체를 사용하는데, 특정 메서드만 가짜로 동작시키거나 감시하는 데 초점이 맞춰져 있습니다. 즉, @Spy는 객체의 전체 동작을 검증하기보다, 일부 동작을 Mocking하면서 특정 메서드가 실제로 호출되었는지, 의도된 대로 실행되는지를 검증하고자 할 때 사용합니다.
@Spy는 실제 객체를 기반으로 하기 때문에, 특정 메서드를 Mocking하여 원하는 값을 반환하거나, 때로는 예외를 던지게 설정할 수도 있습니다.| 기능 | @Mock + @InjectMocks 조합 | @Spy |
|---|---|---|
| 객체 생성 방식 | 가짜 객체로 생성된 의존성을 주입하여 서비스 로직 검증 | 실제 객체를 생성하여 기본적으로 모든 동작을 실제로 수행 |
| 주 목적 | 외부 의존성을 제거하고 서비스 로직 자체만 검증 | 일부 메서드를 Mocking하면서, 특정 메서드가 호출되는지 감시 |
| Mocking 대상 | 의존성(Repository) 등 외부 시스템과의 의존성을 Mocking | 특정 메서드만 Mocking하고 나머지는 실제 동작 수행 |
| 테스트의 초점 | 서비스 로직 검증 (의존성 제거) | 메서드 호출 감시 또는 부분적 Mocking |
@Mock + @InjectMocks: 서비스 로직의 정확성을 검증하고자 할 때 사용합니다. 이 조합은 서비스 레이어의 메서드가 주로 외부 시스템과 의존 관계가 있어도, 외부 시스템을 호출하지 않고 테스트할 수 있게 해줍니다.@Spy: 서비스 또는 다른 레이어의 특정 객체를 감시하거나, 메서드 호출 여부 또는 메서드 결과를 부분적으로 Mocking할 필요가 있을 때 사용합니다. 특정 메서드만 가짜로 동작하도록 설정하면서, 다른 메서드의 실제 동작을 테스트하고 싶을 때 유리합니다.