はじめに
mockkを使ってメソッドの実行回数をテストします。
テスト対象のコード
わかりやすいようにシンプルなクラスを2つ作成しました。
まずはシングルカウントクラスです。
実装はシンプルで貰った引数に+1 or -1 するメソッドが実装されています。
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をコンストラクタの引数にとります。
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の流れがわかりやすいです。)
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を使用してもっと簡単にテストするパターン
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()でなんでもありにしてますが
↓みたいに指定することもできます
verify(exactly = 1) { singleCount.increment(10) }
verify(exactly = 1) { singleCount.increment(11) }
このように書くと10を引数にして1回、11を引数にして1回実行されていることを確認することができます。
まとめ
mockkは便利ですね
もっと使いこなせるようになりたいです。