Mockito-Kotlinの使用例


AndroidプロジェクトでKotlin Mockitを使うメモ

## 参考資料
[Mocking and verifying · nhaarman/mockito-kotlin Wiki](https://github.com/nhaarman/mockito-kotlin/wiki/Mocking-and-verifying)
[Kotlin with Mockito | Baeldung](https://www.baeldung.com/kotlin-mockito)


## build.gradle

以下の依存を追加する

```gradle:build.gradle
dependencies {
...
    testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0-RC1'
    testImplementation 'org.mockito:mockito-android:2.22.0'
...
```

## Mock
モックとは

> ​mockメソッドを使うと、そのクラスの実装に依存しないモックオブジェクトを生成します。
> 各メソッドの戻り値はモックオブジェクトになりますが、指定したメソッドの戻り値だけを任意に設定できます。
[Mockito 初めの一歩](https://qiita.com/mstssk/items/98e597c13f12746c907d)


## 例

### 実行結果を上書きする
関数`test1()`の実行結果を上書きする

```Kotlin:テストコード
    @Test
    fun test1(){
        val testClass = mock<MyTestClass> {
            on{testFunction()} doReturn "mock response"
        }
        assertEquals("mock response",testClass.testFunction())
    }

    class MyTestClass{
        fun testFunction():String{
            return "testCalling inside"
        }
    }
```

### 実行結果を上書きする(呼び出しごとに変化する)
関数`test1()`の実行結果を上書きするが、呼び出しごとに順番に返却する値を変える。

```Kotlin:テストコード
    @Test
    fun testList(){
        val testClass = mock<TestClass> {
            on{test()} doReturn listOf<String>("1","2","3")
        }
        assertEquals("1",testClass.test())
        assertEquals("2",testClass.test())
        assertEquals("3",testClass.test())
    }

    class MyTestClass{
        fun testFunction():String{
            return "testCalling inside"
        }
    }
```



### コールバックインターフェースが呼ばれたか確認する
インターフェース`TestInterface`をモック、TestClassにそのモックインターフェースを渡す。
そのときインターフェースの`onUpdate`が呼ばれていることを確認する。

```Kotlin:テストコード
    @Test
    fun test(){
        val testIf : TestInterface = mock()
        val testClass = TestClass(testIf)
        testClass.test()
        verify(testIf).onUpdate("mocked update") // TestInterface.onUpdateに("haha")が渡された状態になっているか
    }

    interface TestInterface{
        fun onUpdate(str:String)
    }

    class TestClass(val myIf:TestInterface){
        fun test():String{
            myIf.onUpdate("mocked update") // onUpdate実行、"mocked update"返却。
            return "this is test."
        }
    }
```

### コールバックインターフェースの実行結果を上書きする


```Kotlin:テストコード
    @Test
    fun test5(){

        val TEST_STRING = "Can I Create?"
        val EXPECTED_RESULT = "Success"

        val testIf : TestInterface = mock()
        whenever(testIf.create(TEST_STRING)).thenReturn(EXPECTED_RESULT) // インターフェースにTEST_STRINGが渡されたとき、EXPECTED_RESULTを返却するモックを作成

        val testClass = TestClass(testIf)
        val result = testClass.testCreate(TEST_STRING)
        verify(testIf).create(TEST_STRING) // インターフェースが、渡した値で呼ばれているか
        assertEquals(EXPECTED_RESULT, result) // testCreate()の返り値を確認。 ( ≒ whenever節で設定した値でインターフェースが返却されたか )

    }

    interface TestInterface{
        fun create(str:String):String
    }

    class TestClass(val myIf:TestInterface){
        fun testCreate(str: String):String{
            return myIf.create(str)
        }
    }
```


### Spy

一部のメソッドの実行結果のみを上書きし、それ以外はもともとの実装を利用する。

> spyメソッドを使うと、任意のインスタンスの一部だけモックできます。
> spyで得たオブジェクトは、モックしたメソッドの他は元々の実装で動作します。
[Mockito 初めの一歩](https://qiita.com/mstssk/items/98e597c13f12746c907d)

```Kotlin:テスト対象コードとテストコード
    @Test
    fun spy1(){
        val spiedClass = spy(XP1DataTransportClientService.TestClass2())
        spiedClass.testCall()
        verify(spiedClass).testCall2()
    }
    class TestClass2(){
        fun testCall(){
            testCall2()
        }
        fun testCall2(){

        }
    }
```

```Kotlin:テスト対象コードとテストコード
    @Test
    fun passAnyStringToSpy() {
        /* Given */
        val my = spy(MyClass())

        /* When */
        doReturn("mocked").whenever(my).foo(any())

        /* Then */
        expect(my.foo("hello")).toBe("mocked")
    }

    private interface MyInterface {

        fun foo(value: String): String
    }

    private open class MyClass : MyInterface {

        override fun foo(value: String): String = value
    }
```


## いまさら聞けなかったこと

- Mockitoが使えるのはUnit Testのみ。Instrumentation Testは不可。
- `Log.d(...)`ではUnit Testではログは出力されない(あたりまえか)。printlnで。