Kotlin の Object を Mockito + PowerMock でモックする

この記事では、Kotlin の [Object](https://kotlinlang.org/docs/reference/object-declarations.html) という機能を使った部分をモックする手法について説明しています。

# TL; DR
- PowerMock を使うと Kotlin の Object をモックできる
- @JvmStatic を付けると `static` メソッドとしてコンパイルされる
- サンプルプロジェクト: https://github.com/pine613/kotlin-object-mock-sample

# Kotlin とは ?

![Kotlin](https://qiita-image-store.s3.amazonaws.com/0/51394/5f94d31e-e486-2ad4-793e-aa36dcf9144b.png)

Kotlin (ことりん) は [IntelliJ IDEA](https://www.jetbrains.com/idea/) で有名な [JetBrains](https://www.jetbrains.com/) 社が開発している Java との相互運用性を持ったプログラミング言語です。

# Kotlin の Object とは ?
[Singleton パターン](https://ja.wikipedia.org/wiki/Singleton_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3) を簡潔に記述することができる記法です。明示的にインスタンスを生成するコードなどを書かずに Singleton が実現できるため、定形コードを削減することができます。

内部的な実装は、Java で Singleton を実装した場合とほとんど変わりません。

# テスト対象のクラス
## モックしたい Object
`foo` メソッドと `bar` プロパティが存在する `Foo` オブジェクトです。

Kotlin のプロパティである `bar` は Java でいう `getBar` と `setBar` に対応する getter/setter を裏で生成します。

```kotlin:Foo.kt
object Foo {
    fun foo(): String {
        return "foo"
    }

    val bar: String by lazy { "bar" }
}
```

## モック対象 Object を使ってるクラス
`object` で宣言したものは、Singleton として直接呼び出せます。今回は、この `Bar` クラスをテストする時に `Foo` クラスをモックしテストする話をします。

```kotlin:Bar.kt
class Bar {
    fun foo(): String {
        return Foo.foo()
    }

    val bar: String
        get() = Foo.bar
}
```

# テストの際に使うライブラリ
- [Mockito](http://mockito.org/)<br />
使いやすい Java のモックライブラリです。
- [PowerMock](https://github.com/jayway/powermock)<br />
Mockito だとモックできないものもモックできる、とても強い Java のモックライブラリです。

# テストコード
以下で解説しているコードは、サンプルプロジェクトして GitHub 上に公開されていますので、実際に動かしてみる場合はそちらをご利用ください。

https://github.com/pine613/kotlin-object-mock-sample

## `Foo` クラスに対するテスト
普通に JUnit と [kotlin.test](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.test/) を使って書きます。

```kotlin:FooTest.kt
import org.junit.Test
import kotlin.test.assertEquals

class FooTest {
    @Test
    fun foo() {
        assertEquals("foo", Foo.foo())
    }

    @Test
    fun bar() {
        assertEquals("bar", Foo.bar)
    }
}
```

## `Bar` クラスに対するテスト
前準備として、`Foo` クラスをモックでテスト可能にするために `@JvmStatic` アノテーションを追加します。これにより、`Foo` クラスに対しての `static` メンバとしてコードが生成されます (詳細後述)。

```kotlin:Foo.kt
object Foo {
    @JvmStatic
    fun foo(): String {
        return "foo"
    }
    
    @JvmStatic
    val bar: String by lazy { "bar" }
}
```

Mockito と PowerMock を合わせて使います。PowerMock には Mockito 向けのインターフェイスが用意されているので、それを用います。

```kotlin:BarTest.kt
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import kotlin.test.assertEquals

@RunWith(PowerMockRunner::class)
@PrepareForTest(Foo::class)
class BarTest {
    @Test
    fun foo() {
        // Foo.foo() のモック処理
        PowerMockito.mockStatic(Foo::class.java)
        Mockito.`when`(Foo.foo()).thenReturn("xxx_foo")

        // モックを用いて Bar クラスをテスト
        assertEquals("xxx_foo", Bar().foo())

        // モックが呼ばれたことを検証
        PowerMockito.verifyStatic()
        Foo.foo()
    }

    @Test
    fun bar() {
        // Foo.getBar() のモック処理
        // (Kotlin のプロパティは、Java の getter/setter に該当)
        PowerMockito.mockStatic(Foo::class.java)
        Mockito.`when`(Foo.bar).thenReturn("xxx_bar")

        assertEquals("xxx_bar", Bar().bar)

        // モックが呼ばれたことを検証
        PowerMockito.verifyStatic()
        Foo.bar
    }
}
```

### 補足
#### `@RunWith(PowerMockRunner::class)`
このテストクラスを PowerMock を使って実行することを指定します。

#### `@PrepareForTest(Foo::class)`
PowerMock で弄るクラスを指定します。PowerMock でモック化するには、事前にここで指定する必要があります。

#### `PowerMockito.mockStatic(Foo::class.java)`
クラスの `static` メンバをモック可能にします。このメソッドを実行後に Mockito で弄ることができるようになります。

#### `PowerMockito.verifyStatic()`
`static` メンバが呼ばれたかどうか検証を行います。このメソッドを実行後、モックのメソッドを期待する引数で呼び出すことで検証されます。

# `@JvmStatic` アノテーションの働き
Kotlin の Object は、コンパイルすると Singleton パターンのクラスとなります。例えば、以下の `Example.kt` をコンパイルすると、

```kotlin:Example.kt
object Example {
    fun foo(): String {
        return "foo"
    }
}
```

次のようなクラスが生成されます。

```java
public final class Example
{

    public final String foo()
    {
        return "foo";
    }

    private Example()
    {
    }

    public static final Example INSTANCE = (Example)this;
    /**
     * @deprecated Field INSTANCE$ is deprecated
     */
    public static final Example INSTANCE$ = (Example)this;

    static
    {
        new Example();
    }
}
```

Kotlin で `Example.foo()` と記述した場合に実際に呼び出されるのは `Example.INSTANCE.foo()` となります。

この状況で `Example` をモックするには、`INSTANCE` 変数に再度モックしたインスタンスを設定してあげる必要がありますので、モックする手間が増えます (モックは可能ですが)。

`@JvmStatic` アノテーションを付加した場合、以下のように `foo` が `static` メソッドしてコンパイルされます。

```java
public final class Example
{

    public static final String foo()
    {
        return "foo";
    }

    private Example()
    {
    }

    public static final Example INSTANCE = (Example)this;
    /**
     * @deprecated Field INSTANCE$ is deprecated
     */
    public static final Example INSTANCE$ = (Example)this;

    static
    {
        new Example();
    }
}
```

この場合、Kotlin からは直接 `static` メソッドが呼ばれるようにコンパイルされますので、対象の `static` メソッドをモックしてあげるだけで OK です。