본문 바로가기
Kotlin

[Kotlin] MockK 사용법 (4) - Mock 객체 선언 해제(unmockkObject, unmockkStatic, unmockkConstructor, unmockkAll)

by 노력남자 2023. 5. 17.
반응형

이번 포스팅에선 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())
}

 

테스트 성공!

 

 

반응형

댓글