[kotlin] mockkでメソッドの実行回数をテストする

## はじめに
mockkを使ってメソッドの実行回数をテストします。

## テスト対象のコード
わかりやすいようにシンプルなクラスを2つ作成しました。
まずはシングルカウントクラスです。
実装はシンプルで貰った引数に+1 or -1　するメソッドが実装されています。

```kotlin:SingleCount


import org.springframework.stereotype.Component

interface SingleCountInterface{
    fun increment(num:Int):Int
    fun decrement(num:Int):Int
}

@Component
class SingleCount():SingleCountInterface {
    override fun increment(num: Int): Int {
        val result = num + 1
        return result
    }

    override fun decrement(num: Int): Int {
        val result = num - 1
        return result
    }

}
```


次にダブルカウントクラスです。
ここではsingleCountをコンストラクタの引数にとります。

```kotlin:DoubleCount

import org.springframework.stereotype.Component

interface DoubleCountInterface{
    fun doubleIncrement(num:Int):Int
    fun doubleDecrement(num:Int):Int
}

@Component
class DoubleCount(val singleCount: SingleCount):DoubleCountInterface {
    override fun doubleIncrement(num: Int): Int {
       var result = singleCount.increment(num)
        result = singleCount.increment(result)

        return result
    }

    override fun doubleDecrement(num:Int):Int {
        var result = singleCount.decrement(num)
        result = singleCount.decrement(result)

        return result
    }
}
```

これらのコードのうち
DoubleCountの実装のテストとして
・doubleIncrementメソッドでsingleCount.incrementを2回実行していること
・doubleDecrementメソッドでsingleCount.decrementを2回実行していること
これらをテストしたいとします。


## テストコード1
2種類書きます
まずは
@SpringBootTestを使用せずテストするパターン（こちらの方がDIの流れがわかりやすいです。）
```kotlin:テスト1
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Test

class CountClassTest {
    @Test
    fun `incrementメソッドが呼び出された回数を確認`() {
        //mockオブジェクトの作成
        val mockObject = mockk<SingleCount>(relaxed = true)

        //mockオブジェクトを渡してインスタンス化する
        val doubleCount = DoubleCount(mockObject)

        //メソッドを呼び出す
        doubleCount.doubleIncrement(10)

        //メソッドが指定した回数呼び出されたことを確認する
        verify(exactly = 2) { mockObject.increment(any()) }
    }

    @Test
    fun `decrementメソッドが呼び出された回数を確認`() {
        //mockオブジェクトの作成
        val mockObject = mockk<SingleCount>(relaxed = true)

        val doubleCount = DoubleCount(mockObject)

        //メソッドを呼び出す
        doubleCount.doubleDecrement(10)

        //メソッドが指定した回数呼び出されたことを確認する
        verify(exactly = 2) { mockObject.decrement(any()) }
    }
}

``` 

これでそれぞれテストができました。



## テストコード2
次は@SpringBootTestと@SpykBeanを使用してもっと簡単にテストするパターン

```kotlin:テスト2
import com.ninjasquad.springmockk.SpykBean
import io.mockk.verify
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
class CountClassTest2 {
    @Autowired
    private lateinit var doubleCount:DoubleCountInterface

    @SpykBean
    private lateinit var singleCount:SingleCountInterface

    @Test
    fun `incrementメソッドが2回呼び出され12が返ること`() {
        //メソッドを呼び出す
        val result = doubleCount.doubleIncrement(10)

        //メソッドが指定した回数呼び出されたことを確認する
        verify(exactly = 2) { singleCount.increment(any()) }
        assertEquals(12,result)
    }

    @Test
    fun `decrementメソッドが2回呼び出され8が返ること`() {
        //メソッドを呼び出す
        doubleCount.doubleDecrement(10)

        //メソッドが指定した回数呼び出されたことを確認する
        verify(exactly = 2) { singleCount.decrement(any()) }
        assertEquals(8,result)
    }
}

```

だいぶコンパクトになりました。
そしてお気づきかと思いますが
このテストでは SingleCount　をmockに置き換えてないので
処理も正しく行われ assertEqualsで返り値の確認もできます。

めちゃくちゃ簡単にかけますが
色々自動でやってくれちゃうのでDIの流れがわかりにくく最初はなぜこれでテストができるのか戸惑いました。
でも慣れるとこっちの方が楽だと思います。

今は引数はany()でなんでもありにしてますが
↓みたいに指定することもできます
```kotlin:引数の確認
    verify(exactly = 1) { singleCount.increment(10) }
    verify(exactly = 1) { singleCount.increment(11) }
```
このように書くと10を引数にして1回、11を引数にして1回実行されていることを確認することができます。





## まとめ
mockkは便利ですね
もっと使いこなせるようになりたいです。