이것저것

Mock 관련 Annotations

코딩공부 2025. 3. 3. 02:34

 

@Mock

  • Mockito의 Mock 객체 생성
  • 단순한 Mock 객체를 생성하지만, 의존성 주입은 자동으로 하지 않음
  • 메서드 호출 시 기본적으로 null 또는 기본값을 반환
@Mock
private lateinit var emailService: EmailService  // Mock 객체 생성

@Test
fun testEmailSending() {
    `when`(emailService.sendEmail("test@example.com")).thenReturn("Success")

    val result = emailService.sendEmail("test@example.com")

    assertEquals("Success", result) // 반환값 검증
}

 

Mock 객체만 생성되고, UserService 등의 클래스에 자동 주입되지는 않음.

 

@InjectMocks

  • Mock 객체를 주입하여 실제 객체 생성
  • @Mock 또는 @Spy로 생성된 객체를 @InjectMocks가 달린 객체의 의존성으로 자동 주입
  • 단위 테스트에서 의존성을 주입받는 실제 객체를 만들 때 사용
@Mock
private lateinit var emailService: EmailService

@InjectMocks
private lateinit var userService: UserService  // emailService가 자동으로 주입됨

@Test
fun testRegisterUser() {
    val user = User("test@example.com")
    userService.registerUser(user)

    verify(emailService, times(1)).sendEmail(user.email) // Mock 검증
}

 

emailService가 userService에 자동으로 주입됨

 

@MockBean (Spring Boot용)

  • Spring Boot에서 @MockBean을 사용하면, 애플리케이션 컨텍스트에 등록된 빈을 대체
  • @Mock과 달리 Spring ApplicationContext에 등록된 빈을 교체하기 때문에,
    통합 테스트(@SpringBootTest)에서도 사용 가능
  • 단위 테스트보다는 통합 테스트에서 특정 빈(Mock 객체)으로 교체할 때 유용
@SpringBootTest
class UserServiceTest {
    @MockBean
    private lateinit var emailService: EmailService  // Spring 빈을 Mock으로 교체

    @Autowired
    private lateinit var userService: UserService  // emailService가 주입됨

    @Test
    fun testRegisterUser() {
        val user = User("test@example.com")
        userService.registerUser(user)

        verify(emailService, times(1)).sendEmail(user.email) // Mock 검증
    }
}

 

Spring Context에서 emailService 빈을 대체하여 주입

 

@Spy

  • 실제 객체를 생성하고 일부 메서드만 Mock 처리
  • @Mock과 다르게 진짜 객체의 메서드를 호출할 수도 있음
  • 특정 메서드만 when().thenReturn()으로 오버라이드 가능
@Spy
private lateinit var emailService: EmailService  // 실제 객체 사용

@Test
fun testSpyEmailService() {
    doReturn("Success").`when`(emailService).sendEmail("test@example.com")

    val result = emailService.sendEmail("test@example.com")

    assertEquals("Success", result)  // Stub된 값 검증
    verify(emailService, times(1)).sendEmail("test@example.com") // 호출 여부 확인
}

 

@Spy는 진짜 메서드를 호출할 수도 있고, 특정 메서드만 Mock 처리 가능

 

@ExtendWith(MockitoExtension::class) (JUnit 5용)

  • JUnit 5에서 Mockito의 확장 기능을 활성화
  • @Mock, @InjectMocks 등을 사용할 때 MockitoAnnotations.initMocks(this)가 필요 없이 자동으로 초기화
@ExtendWith(MockitoExtension::class)
class UserServiceTest {
    @Mock
    private lateinit var emailService: EmailService

    @InjectMocks
    private lateinit var userService: UserService

    @Test
    fun testRegisterUser() {
        val user = User("test@example.com")
        userService.registerUser(user)

        verify(emailService, times(1)).sendEmail(user.email)
    }
}

 

@ExtendWith(MockitoExtension::class)를 사용하면 Mockito 객체를 자동 초기화

 

 


@Spy

@Mock과 호출하는 방법이 다름

 

@Mock vs @Spy 호출 방법 차이

구분 @Mock @Spy
객체 타입 가짜(Mock) 객체 실제 객체
메서드 기본 동작 기본적으로 null, 빈 값 반환 실제 메서드 실행
Stub 처리 방식 when().thenReturn() 사용 doReturn().when() 사용
실제 메서드 실행 여부 ❌ 실행되지 않음 ✅ 실행됨
사용 목적 테스트 대상의 완전한 대체 일부만 Mock, 나머지는 원래 동작

 

when().thenReturn() vs doReturn().when() 차이

호출 방식 when().thenReturn() doReturn().when()
사용 가능 대상 @Mock @Spy
실제 메서드 실행 여부 ❌ 실행 안 됨 ✅ 원래 메서드 실행 가능
Exception 발생 가능성 @Spy에서 사용하면 실제 메서드가 실행되어
NullPointerException 발생 가능
안전하게 Stub 가능