Help us understand the problem. What is going on with this article?

Mockito を Kotlin で使うなら mockito-kotlin を使おう

More than 1 year has passed since last update.

この記事では、Mockito 自体の使い方などは書きませんのでご注意下さい。
Android向けの記述が多く含まれます。

そのまま使うと起きる問題

Mockito をそのまま Kotlin で使うと以下のようなコードになると思います。

val sharedPreferences = mock(SharedPreferences::class.java)
val editor = mock(SharedPreferences.Editor::class.java, RETURNS_DEEP_STUBS)

`when`(sharedPreferences.edit()).thenReturn(editor)
`when`(editor.commit()).thenReturn(true)
`when`(editor.putString(anyString(), anyString())).thenReturn(editor)

上のコードは、Android の SharedPreferences をモック化するコードです。
sharedPreferences.edit() で更にモック化された Editor を返し、commit()putString() を使えるようにしています。

モックオブジェクトの作成を行う、mock()メソッドはまあいいとして、モックオブジェクトの動作を決めるwhen()メソッドの部分がバッククオートで囲まれており、非常に見にくいです。

これは、Kotlin ではwhen という単語は予約語になってしまうため、バッククオートによるエスケープが必要になってしまうために起きる現象です。

もちろんこのままでも問題は無いのですが、せっかくなので今回はこのモックの作成コードの部分をより Kotlin らしくかけるライブラリ、 mockito-kotlin を使ってみたいと思います。

環境

依存の追加

Gradle や Maven など、それぞれの方法でライブラリの参照を追加します。

// testImplementation とかは環境に合わせる
testImplementation "com.nhaarman:mockito-kotlin:1.5.0"

使い方

モックオブジェクトの作成

引数でクラスを渡すのではなく、ジェネリクスで指定するようになります。

val sharedPreferences = mock<SharedPreferences> {}

第2引数以降で渡していたオプションは、引き続きメソッドの引数に記述します。

val editor = mock<SharedPreferences.Editor>(defaultAnswer = RETURNS_DEEP_STUBS) { }

withSettings()の内容も、全て引数で渡すことができます。

// 使える引数は以下(1.5.0現在)
extraInterfaces: Array<KClass<out Any>>? = null
name: String? = null
spiedInstance: Any? = null
defaultAnswer: Answer<Any>? = null
serializable: Boolean = false
serializableMode: SerializableMode? = null
verboseLogging: Boolean = false
invocationListeners: Array<InvocationListener>? = null
stubOnly: Boolean = false
useConstructor: Boolean = false
outerInstance: Any? = null
stubbing: KStubbing<T>.(T) -> Unit

モックオブジェクトの動作の指定

whenは使わず、モック作成時の関数引数 { } の内部に記述します。
on { モックするメソッド() } で動作をモックするメソッドを指定し、その後に続けて、doReturn, doAnswer, doThrowと続きます。
下のコードは、記事の初めで紹介した Mockito をそのまま使用したコードと等価です。

val editor = mock<SharedPreferences.Editor>(defaultAnswer = RETURNS_DEEP_STUBS) {
    on { commit() } doReturn true
    on { putString(anyString(), anyString()) } doReturn it
}
val sharedPreferences = mock<SharedPreferences> {
    on { edit() } doReturn editor
}

// 以下、記事のはじめに出たコード
val sharedPreferences = mock(SharedPreferences::class.java)
val editor = mock(SharedPreferences.Editor::class.java, RETURNS_DEEP_STUBS)

`when`(sharedPreferences.edit()).thenReturn(editor)
`when`(editor.commit()).thenReturn(true)
`when`(editor.putString(anyString(), anyString())).thenReturn(editor)

doReturn

続けて書いた値がモック対象のメソッドの返り値となります。
返り値がvoidまたはUnitの場合は、Unitを返せば問題ないです。

val stringMock = mock<String> {
    on { toString() } doReturn "mocked"
}
stringMock.toString() // => "mocked"

doAnswer

レシーバとしてモックしたオブジェクトを受け取り、そのメソッドが返すことになっている型を返す関数を記述します。
letみたいなものです。

val stringMock = mock<String> {
    on { toString() } doAnswer { "mocked" + it.hashCode() }
}
stringMock.toString() // => "mocked[数字]"

doThrow

続けて書いた例外を投げます。

val stringMock = mock<String> {
    on { toString() } doThrow RuntimeException()
}
stringMock.toString() // => RuntimeException

おわりに

モックするメソッドの引数の指定の部分では、Mockitoと同様にany()argThat()も使えるので、基本は困ること無いと思います。
他の記述方法などについては、GithubのWikiや、テストのコードを見るとわかりやすいので、書き方わかんねぇと思ったら見るとよいでしょう。

Wiki: https://github.com/nhaarman/mockito-kotlin/wiki/Mocking-and-verifying
テストコード: https://github.com/nhaarman/mockito-kotlin/tree/2.x/tests/src/test/kotlin/test

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした