# Table of Contents

# Mockito

Mockito (opens new window)라이브러리를 사용하면 객체의 Mock을 만들거나, 메소드를 Stubbing하거나, 검증할 수 있다.

# 설정

Mockito를 사용하려면 다음과 같은 의존성을 추가해야한다.

dependencies {
    testImplementation "org.mockito:mockito-core:4.2.0"
    androidTestImplementation "org.mockito:mockito-android:4.2.0"
}

Kotlin에서 Mockito를 사용하려면 mockito-inline도 추가해야한다.

dependencies {
    testImplementation "org.mockito:mockito-inline:4.2.0"
}

# InjectMocks

@InjectMocks mock(List.class)

# Mocking

Mock 객체를 만다는 것을 Mocking한다고 한다.

예제를 살펴보자. Calculator클래스를 다음과 같이 정의하자.

class Calculator {
    fun plus(a: Int, b: Int): Int {
        return a+b
    }

    fun minus(a: Int, b: Int): Int {
        return a-b
    }

    fun multiply(a: Int, b: Int): Int {
        return a*b
    }

    fun divide(a: Int, b: Int): Int {
        return a/b
    }
}

Calculator클래스의 Mock은 다음과 같이 생성한다. Mock 객체가 저장되는 변수에는 @Mock어노테이션을 추가한다. mock()을 호출하면 Mock 객체가 생성된다.

import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.mock

class CalculatorTest {

    @Mock
    lateinit var fakeCalculator: Calculator

    @Test
    fun test_calculator_plus() {
        fakeCalculator = mock(Calculator::class.java)
    }
}

# Stubbing

Calculatoradd()메소드를 Stubbing 해보자. Mock의 plus(3, 7)을 호출하면 10을 반환하도록 Stubbing하고 있다.

class CalculatorTest {

    @Mock
    lateinit var fakeCalculator: Calculator

    @Test
    fun test_calculator_plus() {
        // Create Mock
        fakeCalculator = mock(Calculator::class.java)
        
        // Stubbing
        `when`(fakeCalculator.plus(3, 7)).thenReturn(10)
    }
}

주목할 점은 Mock 객체를 사용하면 메소드의 실제 구현부를 호출하지 않는다는 것이다. Calculatorplus()는 다음과 같이 구현되어있었다.

class Calculator {
    fun plus(a: Int, b: Int): Int {
        return a+b
    }

    // ...
}

그러나 Mock 객체와 Stub를 사용했으므로 10을 반환할 뿐 구현부 { return a+b }가 실행되진 않는다.

이 외에도 Mockito는 Stubbing을 위한 다양한 메소드를 제공한다.

# any()

위에서는 Stubing할 때 3, 7이라는 정확한 값을 넣어주었다.

`when`(fakeCalculator.plus(3, 7)).thenReturn(10)

any()를 사용하면 어떤 값을 넣어도 10을 반환하도록 구현할 수 있다.

`when`(fakeCalculator.plus(anyInt(), anyInt())).thenReturn(10)

anyInt() 외에도 다양한 자료형을 지원하는 메소드가 있다.

  • anyInt()
  • anyString()
  • anyBoolean()
  • anyDouble()
  • anyFloat()
  • anyList()

# thenThrow()

메소드를 호출했을 때 Exception을 발생하도록 Stubbing할 수 있다.

`when`(fakeCalculator.divide(anyInt(), 0).thenThrow(ArithmeticException("by zero"))

# Spying

Mocking이 Mock 객체를 사용했다면 Spying은 실제 객체를 사용한다. Spying은 일부 메소드만 Stubbing할 때 사용할 수 있다.

class CalculatorTest {

    lateinit var calculator: Calculator

    @Test
    fun test_calculator_plus() {

        // Spy Mock
        calculator = spy(Calculator::class.java)

        // Stubbing
        `when`(calculator.plus(anyInt(), anyInt())).thenAnswer {
            30
        }

        val result1 = calculator.plus(5,  20)       // 30
        val result2 = calculator.minus(20, 10)      // 10
        val result3 = calculator.multiply(10, 20)   // 200
    }
}

위 코드에서는 plus()만 Stubbing하고 있다. 따라서 minus()multiply()를 호출하면 실제 구현부인 { return a-b }{ return a*b }가 실행된다.

# 검증

Mockito는 검증을 위한 다양한 메소드를 제공한다.

# verify()

verify()를 사용하면 어떤 메소드가 호출되었는지, 몇 번 호출되었는지 등을 검증할 수 있다.

아래 코드는 fakeCalculator.plus(3, 7)를 세 번 호출한 후 세 번 호출되었는지 검증하고있다.

class CalculatorTest {

    @Mock
    lateinit var fakeCalculator: Calculator

    @Test
    fun test_calculator_plus() {
        // Create Mock
        fakeCalculator = mock(Calculator::class.java)

        // Stubbing
        `when`(fakeCalculator.plus(3, 7)).thenReturn(10)

        // Call plus(3, 7) 3 times
        fakeCalculator.plus(3, 7)
        fakeCalculator.plus(3, 7)
        fakeCalculator.plus(3, 7)

        // Check if called 3 times
        verify(fakeCalculator, times(3)).plus(3, 7)     // Test Success
    }
}

# inOrder()

inOrder()를 사용하면 메소드가 순서대로 호출되었는지 확인할 수 있다.

아래 코드는 fakeCalculator.plus(3, 7), fakeCalculator.minus(10, 4)가 순서대로 호출되었는지 검증하고 있다.

class CalculatorTest {

    @Mock
    lateinit var fakeCalculator: Calculator

    @Test
    fun test_calculator_plus() {
        // Create Mock
        fakeCalculator = mock(Calculator::class.java)

        // Stubbing
        `when`(fakeCalculator.plus(3, 7)).thenReturn(10)
        `when`(fakeCalculator.minus(10, 4)).thenReturn(6)

        // Call Methods
        fakeCalculator.plus(3, 7)
        fakeCalculator.minus(10, 4)

        // Verify Order
        val inOrder = inOrder(fakeCalculator)
        inOrder.verify(fakeCalculator).minus(10, 4)
        inOrder.verify(fakeCalculator).plus(3, 7)

    }
}

# thenAnswer()

thenAnswer()를 사용하면 메소드를 호출했을 때 특정 구문을 호출하도록 Stubbing할 수 있다.

// Create Mock
fakeCalculator = mock(Calculator::class.java)

// Stubbing
`when`(fakeCalculator.plus(anyInt(), anyInt())).thenAnswer {
    30
}

위 코드는 fakeCalculator.plus()를 호출했을 때 30을 반환한다.

# 마치며

Mockito에 대한 더 자세한 내용은 이 곳 (opens new window)에서 확인할 수 있다.