-
👾 MockK와 Mockito🔖 Kotlin 2025. 1. 9. 17:16
📌 Mockito
@Test fun `mockito 테스트 예제`() { // given val username = "beomsic" val user = User(username) given(userRepository.save(any())).willReturn(user) // when userService.save(user) // then then(userRepository).should().save(any()) }
Mockito를 사용하면 BDDMockito를 통해서 BDD 형태로 작성할 수 있다.
- when은 given으로 verify는 then으로 사용
📌 MockK
코틀린에서 테스트 시 Mock 객체를 생성하는 것을 도와주는 라이브러리
@Test fun `mockk 테스트 예제`() { // given val username = "beomsic" val user = User(username) every { userRepository.save(any()) } returns (user) // when userService.save(user) // then verify { userRepository.save(any()) } }
⚠️ Mockito의 문제점
1️⃣ 확장함수
코틀린에서는 확장 함수 기능을 제공을 해주고 있다.
예외 처리 로직을 확장 함수로 정의하여 중복 코드를 제거하고, 더 간결한 코드로 예외를 처리할 수 있도록 한 예시코드
interface TestRepository : JpaRepository<TestEntity, Long> { } fun TestRepository.findEntityById(id: Long): TestEntity { return findByIdOrNull(id) ?: throw NotFoundException() } ---- val entity = testRepository.findEntityById(id)
- kotlin의 확장 함수를 사용해 새로운 기능을 추가
하지만 확장 함수를 사용하면 Mocktio를 사용해 테스트를 할 수 없다.
[Mockito-kotlin Issue] https://github.com/mockito/mockito-kotlin/issues/198
- Mockito으로 Kotlin Extension Function을 Mocking하지 말아라
확장 함수는 정적(static) 메서드이다.
- kotiln의 확장 함수는 실제로 수신 객체의 메서드가 아닌 정적 메서드로 컴파일
Mockito 라이브러리는 정적 메서드를 mocking하는 것을 지원하지 않고 있다.
이와 달리, MockK는 확장함수도 mocking 처리가 가능해 의도대로 테스트를 할 수 있다.
class TestRepositoryExtensionTest { private val testRepository: TestRepository = mockk() @Test fun `findEntityById`() { // given val testEntity = TestEntity(id = 1L, name = "Test Entity") every { testRepository.findEntityById(1L) } returns testEntity // when val result = testService.findById(1L) // then verify { userRepository.findEntityById(1L) } assertThat(result.name).isEqualTo(testEntity.name) } }
2️⃣ object
Kotlin의 object는 프로그램 실행 중 한 번만 초기화되고, 항상 동일한 인스턴스를 사용합니다.
object BCryptUtils { fun hash(password: String) = BCrypt.withDefaults().hashToString(12, password.toCharArray()) fun verify(password: String, hashedPassword: String) = BCrypt.verifyer().verify(password.toCharArray(), hashedPassword).verified }
이렇게 프로젝트 개발시 유틸리티 class나 mapper 형태에서 주로 사용한다.
Mockito는 기본적으로 클래스의 인스턴스를 대상으로 동작
- 클래스의 인스턴스 메서드나 인터페이스 메서드를 가로채(mock)도록 설계
❗object는 정적 필드로 관리되는 싱글톤, object의 메서드는 정적 메서드처럼 작동하기 때문에 정적 멤버나 메서드를 기본적으로 지원하지 않는 Mockito는 mocking을 할 수 없다.
MockK는 kotlin의 특성을 고려해 설계되었기 때문에 object class의 Mocking을 지원한다.
class BCryptUtilsTest { @Test fun `test BCryptUtils hash method`() { mockkObject(BCryptUtils) every { BCryptUtils.hash("password123") } returns "mockedHashedPassword" val hashedPassword = BCryptUtils.hash("password123") assert(hashedPassword == "mockedHashedPassword") unmockkObject(BCryptUtils) } }
- mockkObject() 함수를 이용해 대상 object를 넣어 기본 mocking 방법처럼 적용
- unmockkObject() 를 이용해 테스트 후 mocking 해제를 할 수 있다.
📖 참고자료
'🔖 Kotlin' 카테고리의 다른 글
😎 Infix Function (0) 2024.12.30 널 안정성 (0) 2024.12.29 🦥 lateinit 과 lazy (0) 2024.12.29 Kotlin Coroutines (0) 2024.12.28 Unit / Nothing (0) 2024.11.25