이번 포스팅에선 mock 객체를 해제하는 방법에 대해 알아보겠다.
mock 객체 해제(= unmock)란?
mock 객체를 해제한다는 건 mock 객체 이전 일반 객체로 돌려놓는다는 의미다.
unmock은 언제 사용해야 할까?
이전 포스팅에서 소개한 mockkObject, mockkStatic, mockkConstructor를 스터빙하면 테스트 전체에 영향이 가기 때문에 각 테스트가 끝나면 unmock을 해줘야 한다.
아래 테스트를 보자. 전부 통과해야 하는 테스트다.
// LocalDate.now(): 2023-05-17
@Test
fun testMockkStatic1() {
mockkStatic(LocalDate::class)
every { LocalDate.now() } returns LocalDate.of(2023, 5, 16)
assertEquals(LocalDate.of(2023, 5, 16), LocalDate.now())
}
@Test
fun testMockkStatic2() {
assertEquals(LocalDate.of(2023, 5, 17), LocalDate.now())
}
어라? testMockkStatic2가 실패했네? LocalDate.now()가 testMockkStatic1에서 스터빙한 값으로 리턴됐네..
testMockkStatic1에서 스터빙한 LocalDateTime.now()가 testMockkStatic2까지 영향을 미친다.
이걸 해결할 때 unmockk을 사용해야 한다.
unmock을 하지 않고 스터빙을 해제하는 clear를 사용할 수도 있긴한데 이거에 대해선 뒤에서 다루겠다.
unmockkObject
mockkObject를 unmock할 때 사용
unmockkObject(mockkObject)
이전에 포스팅에서 사용했던 Membership으로 테스트를 해보자.
enum class Membership(
val label: String
) {
BRONZE("브론즈"),
SILVER("실버"),
GOLD("골드");
}
아래 테스트는 당연하게 통과할 거다.
@Test
fun testEnum1() {
mockkObject(Membership.BRONZE)
every { Membership.BRONZE.label } returns "골드"
assertEquals("골드", Membership.BRONZE.label)
}
@Test
fun testEnum2() {
assertEquals("브론즈", Membership.BRONZE.label)
}
하지만 실패했다. testEnum1에서 스터빙한 게 testEnum2에 영향을 줬다.
testEnum1이 끝나면 Membership.BRONZE를 unmock해줘야 한다.
각 테스트가 끝나면 @BeforeEach를 써서 unmockkObject에 Membership.BRONZE를 unmock 해주자.
@BeforeEach // 추가
fun beforeEach() {
unmockkObject(Membership.BRONZE)
}
@Test
fun testEnum1() {
mockkObject(Membership.BRONZE)
every { Membership.BRONZE.label } returns "골드"
assertEquals("골드", Membership.BRONZE.label)
}
@Test
fun testEnum2() {
assertEquals("브론즈", Membership.BRONZE.label)
}
테스트 성공!
unmockkStatic
mockkStatic을 unmock할 때 사용
위에서 사용했던 예제를 이제 해결해보자.
// LocalDate.now(): 2023-05-17
@Test
fun testMockkStatic1() {
mockkStatic(LocalDate::class)
every { LocalDate.now() } returns LocalDate.of(2023, 5, 16)
assertEquals(LocalDate.of(2023, 5, 16), LocalDate.now())
}
@Test
fun testMockkStatic2() {
assertEquals(LocalDate.of(2023, 5, 17), LocalDate.now())
}
testMockkStatic1에서 스터빙한 값이 testMockkStatic2에 영향을 줘서 실패했다.
이걸 해결하려면 각 테스트가 끝날 때 LocalDate를 unmock해줘야 한다.
@BeforeEach // 추가
fun beforeEach() {
unmockkStatic(LocalDate::class)
}
@Test
fun testMockkStatic1() {
mockkStatic(LocalDate::class)
every { LocalDate.now() } returns LocalDate.of(2023, 5, 16)
Assertions.assertEquals(LocalDate.of(2023, 5, 16), LocalDate.now())
}
@Test
fun testMockkStatic2() {
Assertions.assertEquals(LocalDate.of(2023, 5, 17), LocalDate.now())
}
테스트 성공!
unmockkConstructor
mockkConstructor를 unmock할 때 사용
unmockkConstructor(mockkConstructor)
아래 테스트를 보자. 통과할까?
class MockCls(private val a: Int = 0) {
fun add(b: Int) = a + b
}
@Test
fun testConstructor1() {
mockkConstructor(MockCls::class)
every { constructedWith<MockCls>(OfTypeMatcher<Int>(Int::class)).add(1) } returns 2
assertEquals(2, MockCls().add(1))
}
@Test
fun testConstructor2() {
assertEquals(1, MockCls().add(1))
}
어림없다. testConstrcutor1에서 스터빙한 값이 testConstructor2에 영향을 줬다.
이걸 해결하려면 각 테스트가 끝날 때 MockCls의 mockkConstructor를 unmock해줘야 한다.
@BeforeEach에서 unmockkConstructor를 호출해주자.
@BeforeEach // 추가
fun beforeEach() {
unmockkConstructor(MockCls::class)
}
@Test
fun testConstructor1() {
mockkConstructor(MockCls::class)
every { constructedWith<MockCls>(OfTypeMatcher<Int>(Int::class)).add(1) } returns 2
assertEquals(2, MockCls().add(1))
}
@Test
fun testConstructor2() {
assertEquals(1, MockCls().add(1))
}
테스트 성공!
unmockkAll
mockkObject, mockkStatic, mockkConstructor 전부를 unmock해준다.
unmockkAll()
이전 unmockk 메소드를 호출하면서 정말 불편했다. 특정 mockk 객체를 unmock 해달라고 요청했어야 하니까!!
근데 특정 mockk개체만 unmock하는 거도 필요는 하니 잘 쓰면된다.
위에서 예로 들었던 문제의 테스트들을 unmockkAll을 사용해서 해결한 코드다.
unmockkAll() 하나만 호출하면 되니 속시원하다.
@BeforeEach
fun beforeEach() {
unmockkAll()
}
@Test
fun testConstructor1() {
mockkConstructor(MockCls::class)
every { constructedWith<MockCls>(OfTypeMatcher<Int>(Int::class)).add(1) } returns 2
Assertions.assertEquals(2, MockCls().add(1))
}
@Test
fun testConstructor2() {
Assertions.assertEquals(1, MockCls().add(1))
}
@Test
fun testEnum1() {
mockkObject(Membership.BRONZE)
every { Membership.BRONZE.label } returns "골드"
Assertions.assertEquals("골드", Membership.BRONZE.label)
}
@Test
fun testEnum2() {
Assertions.assertEquals("브론즈", Membership.BRONZE.label)
}
@Test
fun testMockkStatic1() {
mockkStatic(LocalDate::class)
every { LocalDate.now() } returns LocalDate.of(2023, 5, 16)
Assertions.assertEquals(LocalDate.of(2023, 5, 16), LocalDate.now())
}
@Test
fun testMockkStatic2() {
Assertions.assertEquals(LocalDate.of(2023, 5, 17), LocalDate.now())
}
테스트 성공!
'Kotlin' 카테고리의 다른 글
[Kotlin] Sealed Class, Interface 사용법 (1) - Sealed Class, Interface란? (0) | 2023.06.12 |
---|---|
[Kotlin] 공식 코틀린 코딩 컨벤션 소개 (0) | 2023.06.06 |
[Kotlin] MockK 사용법 (3) - Mock 객체 선언 방법 (mockkClass, mockkObject, mockkStatic, mockkConstructor) (0) | 2023.05.15 |
[Kotlin] MockK 사용법 (2) - Mock 객체 선언 방법 (mockk<T>, spyk<T>, spyk(obj)) (0) | 2023.05.12 |
[Kotlin] MockK 사용법 (1) - MockK란? (0) | 2023.05.01 |
댓글