환경
IDE: intellij
SpringBootVersion: 2.7.18
Gradle: 8.5
Java: 17
해당 포스팅은
인프런 "코틀린 문법부터 실무까지 (자바 to 코틀린 실무)" 을 따라하면서 Java/Spring 프로젝트를 Kotlin/Spring 프로젝트로
점진적인으로 변환하는 내용을 담고 있습니다
다루는 내용
- Mockito -> MockK
MockK
@MockK:
- @Mock 대응
- 더 엄격한 테스트 환경을 제공합니다. 모든 동작을 명시적으로 정의해야 합니다.
- 모든 상호작용을 정확히 제어하고 싶을 때 사용합니다.
@RelaxedMockK: - 더 유연한 테스트 환경을 제공합니다. 필요한 동작만 정의할 수 있습니다.
- 일부 동작만 모킹하고 나머지는 무시하고 싶을 때 유용합니다.
@InjtectMockKs:
- @InjectMocks 대응
의존성 추가
val mockkVersion = "1.13.8"
testImplementation("io.mockk:mockk:${mockkVersion}")
JUnit5 테스트 코드 수정
java -> kotlin
nullable한 객체를 참조할때는 safe call operator를 이용해서 참조하는 객체가 null이 아닐때만 호출하거나 프로퍼티에 접근하도록 합니다
typescript의 optional chainning과 매우 유사한 개념이고, 사용방법도 동일합니다
MockitoExtension -> MockKExtension 적용
테스트 코드를 작성할때 외부 의존성이 필요한경우 lateinit var를 사용합니다
MockKExtension이 테스트 인스턴스 생성할때 주입되는 필드들을 목 객체로 만들어주고 생성할때 주입해줍니다
// AS-IS
BDDMockito.given(somethingRepository.findById(ArgumentMatchers.anyString()))
.willReturn(Optional.of(something))
// TO-BE
every { somethingRepository.findById(any())
} returns Optional.of(something)
// AS-IS
val captor = ArgumentCaptor.forClass(Something::class.java)
// TO-BE
var slot = slot<Something>()
// AS-IS
Mockito.verify(somethingRepository, Mockito.times(1))
?.save(captor.capture())
val savedSomething = captor.value
// TO-BE
verify(exactly = 1) { somethingRepository.save(capture(slot)) }
val savedSomething = slot.captured
MockK는 호출 대상에 대한 스텁 정의를 하지 않으면 오류를 발생합니다
io.mockk.MockKException: no answer found for: ~~
@RelxedMockK를 통해 모든 메서드의 스텁을 하지 않도록 할 수 있습니다
참고: https://javacan.tistory.com/entry/kotlin-mock-framework-mockk-intro
java.lang.ClassCastException: class java.lang.Object cannot be cast to class
스텁을 해줘야 테스트가 가능한경우에는 다음과 같이 스텁잉 해줍니다
every { somethingRepository.save(any())} returns SomeThing(
id = null,
name = "Ismael Salas",
age = 8727,
createdAt = null,
updatedAt = null
)
capture
MockK에서 capture를 사용하기 위해선 slot을 생성합니다
var slot = slot<Prince>()
Capture 사용합니다
이 단계에서 save 메서드에 전달된 인자가 slot에 캡처됩니다.
verify { somethingRepository.save(capture(slot)) }
캡처된 값 접근
val savedPrince = slot.captured
여러 객체를 캡처할때
// 가정: Something 클래스가 있고, 이를 생성하는 SomethingService가 있습니다.
class Something(
val id: String,
val type: String,
val value: Int
)
class CreateSomething {
data class Request(val type: String, val value: Int)
}
@Test
fun createMultipleSomethingsTest() {
// Given
val request1 = CreateSomething.Request("typeA", 10)
val request2 = CreateSomething.Request("typeB", 20)
// 여러 객체를 캡처하기 위한 리스트 생성
val capturedSomethings = mutableListOf<Something>()
// 모의 객체 설정
every { somethingRepository.save(any()) } returns mockk()
// When
somethingService.createSomething(request1)
somethingService.createSomething(request2)
// Then
// save 메서드가 정확히 2번 호출되었는지 확인하고 각 호출의 인자를 캡처
verify(exactly = 2) { somethingRepository.save(capture(capturedSomethings)) }
// 첫 번째 캡처된 Something 객체 검증
assertThat(capturedSomethings[0]).apply {
prop(Something::type).isEqualTo("typeA")
prop(Something::value).isEqualTo(10)
}
// 두 번째 캡처된 Something 객체 검증
assertThat(capturedSomethings[1]).apply {
prop(Something::type).isEqualTo("typeB")
prop(Something::value).isEqualTo(20)
}
}
'스프링 프레임워크 > kotlin' 카테고리의 다른 글
kotlin-springboot spring-kafka로 produce, consume 시작하기 (0) | 2024.08.12 |
---|---|
Java/Spring -> Kotlin/Spring 변환 - 4 (0) | 2024.08.07 |
Java/Spring -> Kotlin/Spring 변환 - 3 (0) | 2024.07.28 |
Java/Spring -> Kotlin/Spring 변환 - 1 (0) | 2024.07.24 |