ユニットテストでmocking frameworkにMockito 2.13を利用するサンプルコード

# 概要

Kotlinを利用したプロジェクトのユニットテストでMockフレームワークにMockito 2.13を使ったサンプルコードです。

**環境**

* Windows 10 Professional
* Java 1.8.0_162
* Kotlin 1.2.21
* JUnit 4.12
* Mockito 2.13.0
* IntelliJ IDEA 2017.3

**参考**

* [mockito] (http://site.mockito.org/)
* [mockito/mockito - GitHub Wiki] (https://github.com/mockito/mockito/wiki)
* [Mockito 2.13.0 API] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/Mockito.html)

# 導入

```groovy
dependencies {

    testCompile("org.mockito:mockito-core:2.13.0")
}
```

テストクラスで、Mockitoクラスのstaticメソッドをimportしておくとコード量が減って見やすくなります。

```kotlin
import org.mockito.Mockito.*
```

## final class/methodをmock/spy化できるようにする

デフォルトではfinal class/methodはmock/spy化できませんが、[39. Mocking final types, enums and final methods (Since 2.1.0)] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/Mockito.html#39)にあるように、Mockito 2.1.0から下記の設定を行うことでfinal class/methodのmock/spy化ができるようになっています。

**手順**

以下の場所にmockito-extensionsというディレクトリを作成します。

```text
test/resources
```

そこへ`org.mockito.plugins.MockMaker`という名前のテキストファイルを作成し下記の1行を追加します。

```text
mock-maker-inline
```
これでfinalなclassもmock/spy化できますが、Stringなど特定のクラスはmock/spy化できないようです。

```text
org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class java.lang.String
Mockito cannot mock/spy because :
 - Cannot mock wrapper types, String.class or Class.class
```

# オブジェクトを初期化する

Mock、Spy、Captor、InjectMocksアノテーションが付いたオブジェクトの初期化を行う方法です。
下記の方法があり状況に応じて選択できます。

## MockitoJUnitRunnerを使用する

[Class MockitoJUnitRunner] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/junit/MockitoJUnitRunner.html)

```kotlin
@RunWith(MockitoJUnitRunner::class)
class Tests {
} 
```

**推奨する設定 (Highly recommended)**

> It is highly recommended to use MockitoJUnitRunner.StrictStubs variant of the runner. It drives cleaner tests and improves debugging experience.

```kotlin
@RunWith(MockitoJUnitRunner.StrictStubs::class)
```

**StrictStubsにすると**

引数のミスマッチを検出します。
次のようなstubの設定と実際の呼び出しに引数の違いがあった場合

```kotlin
`when`(mock.sumAndApplyRate(arrayOf(1, 2, 3))).thenReturn(60)

mock.sumAndApplyRate(arrayOf(1, 2, 4)).also { println(it) }
```

例外がスローされます。

```text
org.mockito.exceptions.misusing.PotentialStubbingProblem: 
Strict stubbing argument mismatch. Please check:
 - this invocation of 'sumAndApplyRate' method:
    fugaLogic.sumAndApplyRate([1, 2, 4]);
    -> at com.example.exercise.mockito.BasicMockitoExerciseTests.test demo mock3(BasicMockitoExerciseTests.kt:200)
 - has following stubbing(s) with different arguments:
    1. fugaLogic.sumAndApplyRate([1, 2, 3]);
      -> at com.example.exercise.mockito.BasicMockitoExerciseTests.test demo mock3(BasicMockitoExerciseTests.kt:196)
```

呼び出されないstubメソッドを検出します。
設定したstubメソッドが呼び出されないと例外がスローされます。

```text
org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at com.example.exercise.mockito.BasicMockitoExerciseTests.test demo mock2(BasicMockitoExerciseTests.kt:175)
Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class.
```

## MockitoRuleを使用する

[Interface MockitoRule] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/junit/MockitoRule.html)

```kotlin
class Tests {

    @Rule
    @JvmField
    val rule: MockitoRule = MockitoJUnit.rule()

}
```

**推奨する設定 (Highly recommended)**

テストランナーと同じ設定がルールでも行えます。
Incubatingアノテーションが付いているので将来仕様が変わることがあります。

```kotlin
@Rule
@JvmField
val rule: MockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS)
```

## MockitoAnnotations.initMocksを使用する

テストランナーやルールを使用しない場合はinitMocksで初期化することができます。

```kotlin
class Tests {

    @Mock
    private lateinit var hoge: Hoge
    @Spy
    private lateinit var fuga: Fuga

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
    }

}
```

## 上記のいずれの方法も利用しない

アノテーションを使わずにmock/spyオブジェクトを手動で生成し、テスト対象のオブジェクトに手動で設定（注入）するという方法もあります。

```kotlin
@Test
fun `test`() {

    val bar = mock(Bar::class.java)
    val foo = spy(FooImpl(123))

    val sut = MockDemoServiceImpl(bar, foo)

    //テストする
}
```

# Mockオブジェクト

テスト対象のオブジェクトが依存するオブジェクトの代用オブジェクトで、テスト対象が依存オブジェクトの呼び出しを期待した通りに行ったか検証する目的で利用します。
メソッドの戻り値を予め決めた値にしたり特定の引数で例外をスローさせたりとオブジェクトの振る舞いを制御することもできます。
またインターフェースもmock化できるので実装クラスがなくてもテストが可能という利点もあります。
Mockitoの場合、mock化したオブジェクトのメソッドはデフォルトでstub（呼び出しに対して予め決めた値を返す）なので、stubオブジェクトの性質もあわせもっているといえます。

## デフォルトの戻り値

mock化したオブジェクトのメソッドの戻り値は、型毎にデフォルトが決まっています。どのような値を戻すかはオブジェクトをmock化する際に指定することができます。
通常はデフォルト(RETURNS_DEFAULTS)の設定でいいようですが、Mockito 3.0でデフォルトになる予定のRETURNS_SMART_NULLSという「nullの代わりになるべくSmartNullを返す」設定もあります。

**RETURNS_SMART_NULLSの設定例**

```kotlin:Mockアノテーション
@Mock(answer = Answers.RETURNS_SMART_NULLS)
private lateinit var bar: Bar
```

```kotlin:mockメソッド
mock(Bar::class.java, Answers.RETURNS_SMART_NULLS)
```

指定できる種類は[Enum Answers] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/Answers.html)で定義されています。

以下の2つについて、戻り値の型毎にどのようなデフォルト値を返すか表にまとめました。

* RETURNS_DEFAULTS (デフォルト)
* RETURNS_SMART_NULLS

|           |RETURNS_DEFAULTS |RETURNS_SMART_NULLS |
|:----------|:----------------|:-------------------|
|String     | null            | ""                 |
|String?    | null            | ""                 |
|Int        | 0               | 0                  |
|Int?       | 0               | 0                  |
|Date       | null            | <b>SmartNull</b>   |
|Date?      | null            | <b>SmartNull</b>   |
|LocalDate  | null            | null               |
|LocalDate? | null            | null               |
|Boolean    | false           | false              |
|Boolean?   | false           | false              |
|Array      | null            | size 0             |
|Array?     | null            | size 0             |
|List       | size 0          | size 0             |
|List?      | size 0          | size 0             |
|Pair       | null            | null               |
|Pair?      | null            | null               |
|CustomData | null            | <b>SmartNull</b>   |
|CustomData?| null            | <b>SmartNull</b>   |

* Pair : Kotlinの標準ライブラリの[Piar] (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/index.html)のことです。
* CustomData : 検証用に用意したクラスです。
    * `open class CustomData(val id: Long, val name: String)`

**RETURNS_DEFAULTS**

メソッドの戻り値の型がプリミティブの場合はその型の初期値、コレクションはempty、オブジェクトの場合はnullを返します。

**RETURNS_SMART_NULLS**

> ReturnsSmartNulls first tries to return ordinary values (zeros, empty collections, empty string, etc.) then it tries to return SmartNull. If the return type is final then plain null is returned.

RETURNS_DEFAULTSではStringの場合はnullを返しますが、RETURNS_SMART_NULLSでは空文字を返すように、極力nullを返さないようになっています。
オブジェクト型もnullの代わりになるべくSmartNullを返すようになりますが、final修飾子が付いている場合はnullになります。上記の表でLocalDateやPairがSmartNullにならないのは、このクラスにfinalが付いているためです。

```text:signature
public final class LocalDate
        implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable
```

SmartNullを返すメソッドの戻り値を使用するとNullPointerExceptionの代わりに、下記のようにSmartNullPointerExceptionがスローされます。
エラーメッセージもNPEより詳しく出力されるので原因の確認が効率的にできます。

```kotlin
org.mockito.exceptions.verification.SmartNullPointerException: 
You have a NullPointerException here:
-> at com.example.exercise.mockito.BasicMockitoExerciseTests.test hoge(BasicMockitoExerciseTests.kt:97)
because this method call was *not* stubbed correctly:
-> at com.example.exercise.mockito.BasicMockitoExerciseTests.test hoge(BasicMockitoExerciseTests.kt:96)
hogeLogic.getDate();
```

## mockオブジェクトの生成

### Mockアノテーションを使う方法

[Annotation Type Mock] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/Mock.html)

```text
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Documented
public @interface Mock
```

```kotlin:例
@Mock
private lateinit var bar: Bar
```

mockオブジェクトに名前を付けます。付けた名前はデバッグで利用できます。

```kotlin:例
@Mock(name = "name of bar")
private lateinit var bar: Bar
```

Answerを設定します。

```kotlin:例
@Mock(answer = Answers.RETURNS_SMART_NULLS)
private lateinit var bar: Bar
```

### Mockito.mockメソッドを使う方法

[Mockito.mock(Class)] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/Mockito.html#mock-java.lang.Class-)

```text:signature
public static <T> T mock(Class<T> classToMock)
public static <T> T mock(Class<T> classToMock, String name)
public static <T> T mock(Class<T> classToMock, Answer defaultAnswer)
public static <T> T mock(Class<T> classToMock, MockSettings mockSettings)
```

アノテーションで生成するのと同様です。

```kotlin:例
private val bar = mock(Bar::class.java)

private val bar = mock(Bar::class.java, "name of bar")

private val bar = mock(Bar:class.java, Answers.RETURNS_SMART_NULLS)
```

## メソッドの振る舞いを設定する

whenメソッドでメソッドが任意の値を返すようにしたり、例外をスローさせたりといった振る舞いを設定します。

### メソッドの戻り値を設定する (thenReturn)

実行時の現在日時を返すようなメソッドでも固定した日時を返すようにすることができます。

```kotlin
`when`(hoge.currentDateTime()).thenReturn(LocalDateTime.of(2018,2,1,0,0,0))

hoge.currentDateTime().also { println(it) }
// 2018-02-01T00:00
```

メソッドの戻り値を100という固定値に設定します。

```kotlin
`when`(hoge.count).thenReturn(100)

hoge.count.also { println(it) }
// 100
```

### メソッドの呼び出し毎に戻り値を変える

thenReturnに複数の値を設定することで呼び出し毎に戻り値を変えます。

```kotlin
`when`(hoge.count).thenReturn(100, 101)

hoge.count.also { println(it) }
// 100
hoge.count.also { println(it) }
// 101
hoge.count.also { println(it) }
// 101
```

### 引数の値によって特定の値を返す

メソッド呼び出し時に引数のマッチングで特定の値を返すようにするにはArgumentMatchersクラスを使用します。

この例では引数がaから始まる文字列の場合に1を返すように振る舞いを設定しています。
この条件にマッチしない呼び出しの時はInt型のデフォルト値の0が返ります。

```kotlin
`when`(hoge.fruits(startsWith("a"))).thenReturn(1)

hoge.fruits("apple").also { println(it) }
// 1
hoge.fruits("banana").also { println(it) }
// 0
```

1つのメソッドに複数の振る舞いを持たせる場合は少し記述方法が変わります。
ポイントは書き方がwhen().thenReturn()ではなくdoReturn().when()になるのと、マッチングの範囲が最も広い条件を先頭にすることです。

```kotlin
doReturn(3).`when`(hoge).fruits(anyString())
doReturn(1).`when`(hoge).fruits(startsWith("a"))
doReturn(2).`when`(hoge).fruits(startsWith("b"))

hoge.fruits("apple").also { println(it) }
// 1
hoge.fruits("banana").also { println(it) }
// 2
hoge.fruits("cherry").also { println(it) }
// 3
hoge.fruits("durian").also { println(it) }
// 3
```

### メソッド呼び出しで例外をスローさせる (thenThrow)

メソッド呼び出し時に任意の例外をスローさせます。引数の値で例外をスローさせるかどうかも設定することができます。

この例では引数が0ときに例外をスローさせます。なお、引数が0以外の場合は条件に一致しないので例外はスローされません。

```kotlin
`when`(hoge.initCount(0)).thenThrow(IllegalStateException())

hoge.initCount(0)
// IllegalStateExceptionがスローされる

hoge.initCount(1)
// 例外はスローされない
```

ArgumentMatchers.anyInt()を使うと任意の数値がマッチします。

```kotlin
`when`(hoge.initCount(anyInt())).thenThrow(IllegalStateException())

hoge.initCount(100)
// IllegalStateExceptionがスローされる
```

メソッド呼び出しのx回目で例外をスローさせるということもできます。
この例では2回目のメソッド呼び出しで例外をスローさせます。
ちなみにdoNothingを使っているのはメソッドが戻り値を返さないためです。

```kotlin
doNothing().doThrow(IllegalStateException()).`when`(hoge).initCount(anyInt())

hoge.initCount(10)
hoge.initCount(20)
// IllegalStateExceptionがスローされる
```

## メソッドの呼び出しを検証する

verifyメソッドでテスト対象がmock化した依存オブジェクトを正しく呼び出しているか検証します。

メソッドが呼び出されたことを検証

```kotlin
verify(hoge).countUp()
```

メソッドがx回呼び出されたことを検証

```kotlin
verify(hoge, times(2)).countUp()
```

メソッドが最大x回まで呼び出されたことを検証

```kotlin
verify(hoge, atMost(2)).countUp()
```

メソッドが最低x回以上呼び出されたことを検証

```kotlin
verify(hoge, atLeast(2)).countUp()
```

このメソッドだけが呼び出されたことを検証

```kotlin
verify(hoge, only()).countUp()
```

メソッドが呼び出されなかったことを検証

```kotlin
verify(hoge, never()).countUp()
```

検証が失敗した時の説明を付ける

```kotlin
verify(hoge, never().description("このメソッドはxxxの時、呼び出されない")).countUp()
```

```text
org.mockito.exceptions.base.MockitoAssertionError: このメソッドはxxxの時、呼び出されない

hogeLogic.countUp();
Never wanted here:
-> at com.example.exercise.mockito.HogeLogic.countUp(HogeLogic.kt:102)
But invoked here:
-> at com.example.exercise.mockito.BasicMockitoExerciseTests.test demo mock5(BasicMockitoExerciseTests.kt:225)
```


## 部分的なモック化 (Real partial mocks)

特定のstubメソッドの振る舞いを[thenCallRealMethod] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/stubbing/OngoingStubbing.html#thenCallRealMethod--)にすると、そのメソッドを呼び出したときに実際のオブジェクトのメソッドを呼び出します。

ただし、JavaDocのサンプルコードのコメントに注意書きがあるようにオブジェクトの状態に依存しているメソッドは、期待しない結果になる場合があります。

```kotlin
//you can enable partial mock capabilities selectively on mocks:
val mock = Mockito.mock(Foo::class.java)

//Be sure the real implementation is 'safe'.
//If real implementation throws exceptions or depends on specific state of the object then you're in trouble.
`when`(mock.someMethod()).thenCallRealMethod()
```

# stubオブジェクト

mockオブジェクトと似た性質を持っていますが、メソッド呼び出しの検証はできません。
なので、依存オブジェクトの振る舞いを制御可能な状態にするだけで検証は不要な場合に利用します。
MockitoにはStubアノテーションやMockito.stubというメソッドがないので、明示的にStubオブジェクトというものは作れません。

## stubオブジェクトの生成

Mockitoでは以下の方法でstubと呼べるオブジェクトを生成できます。

### Mockアノテーションを使う方法

```kotlin:例
@Mock(stubOnly = true)
private lateinit var bar: Bar
```

### Mockito.mockメソッドを使う方法

```kotlin:例
private val bar = mock(Bar::class.java, withSettings().stubOnly())
```

## メソッドの振る舞いを設定する

mockオブジェクトと同様です。

## メソッドの呼び出しを検証する

stubオブジェクトなのでverifyメソッドで検証することはできません。
コンパイルエラーにはなりませんが、verifyで検証しようとすると例外がスローされます。

```text
org.mockito.exceptions.misusing.CannotVerifyStubOnlyMock: 
Argument passed to verify() is a stubOnly() mock, not a full blown mock!
If you intend to verify invocations on a mock, don't use stubOnly() in its MockSettings.
```

# spyオブジェクト

mockオブジェクトと違いspyオブジェクトはオブジェクトの状態を持っているので、依存オブジェクトに対する副作用も含めて検証する場合に利用します。

## spyオブジェクトの生成

mockオブジェクトと違いspyオブジェクトの生成には実際の依存オブジェクトが必要です。
Spyアノテーションで生成する場合はデフォルトコンストラクタが必要です。
デフォルトコンストラクタがない場合は、手動でオブジェクトを生成して初期化する必要があります。

### Spyアノテーションを使う方法

[Annotation Type Spy] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/Spy.html)

```text
@Retention(RUNTIME)
@Target(FIELD)
@Documented
public @interface Spy
```

デフォルトコンストラクタがある場合はMockitoがオブジェクトを生成してくれます。

```kotlin:例
@Spy
private lateinit var bar: Bar
```

オブジェクトの生成に引数が必要な場合は手動で生成します。

```kotlin:例
@Spy
private val foo = Far("argument")
```

### Mockito.spyメソッドを使う方法

[Mockito.spy(Class)] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/Mockito.html#spy-java.lang.Object-)

```text:signature
public static <T> T spy(T object)
```

spyメソッドを使う場合はいずれの場合も手動でオブジェクトを生成する必要があります。

```kotlin:例
private val bar = spy(Bar())
```

```kotlin:例
private val foo = spy(Foo("argument"))
```

## メソッドの振る舞いをstub化する

mockオブジェクトと同様にspyオブジェクトのメソッドもstub化できますが注意する点があります。

1つ目は、MockitoのAPIリファレンスにも注意書きがあるとおり、以下のようなコードだとwhenメソッドの実行時にIndexOutOfBoundsExceptionがスローされます。

```kotlin
val list = mutableListOf<String>()
val spyList = spy(list)

`when`(spyList[0]).thenReturn("FOO")
// java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

val actual = spyList[0]

assertThat(actual).isEqualTo("FOO")
verify(spyList, times(1))[0]
```

代わりに下記のように記述すると例外は発生しません。

```kotlin
doReturn("FOO").`when`(spyList)[0]
```

もう1つは、stub化したメソッドがオブジェクトの状態を変えるような場合、その副作用は起きなくなります。

```kotlin
val list = mutableListOf<String>("foo", "bar", "qix")
val spyList = spy(list)

doNothing().`when`(spyList).clear()

spyList.clear()

spyList.joinToString().also { println(it) }
// foo, bar, qix
```

# テスト対象オブジェクトの生成

テスト対象(SUT / System under test)オブジェクトの生成時に、mock/spyオブジェクトを注入するにはInjectMocksアノテーションを使う方法があります。
mock/spyオブジェクトの注入には、コンストラクタインジェクション、セッターインジェクション、フィールドインジェクションの方法があります。

## InjectMocksアノテーションを使う方法

[InjectMocks] (https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/InjectMocks.html)

```text
@Documented
@Target(value=FIELD)
@Retention(value=RUNTIME)
public @interface InjectMocks
```

```kotlin:例
@Mock
private lateinit var bar: Bar
@Spy
private val foo: Foo = FooImpl(123)

@InjectMocks
private lateinit var sut: MockDemoServiceImpl
```

# 補足

## Mockito 2.xの制約

[What are the limitations of Mockito] (https://github.com/mockito/mockito/wiki/FAQ#what-are-the-limitations-of-mockito)

* Requires Java 6+
* 静的メソッドはmockできない
* コンストラクタはmockできない
* equals、hashCodeメソッドはmockできない
* モックは[ObjenesisによってサポートされているVM] (https://github.com/easymock/objenesis/blob/2.5/SupportedJVMs.md)でのみ可能
* Spying on real methods where real implementation references outer Class via OuterClass.this is impossible.