LoginSignup
7
4

More than 5 years have passed since last update.

Kotlin の Suspending 関数を Mockito で spy する

Last updated at Posted at 2019-04-03

mockito-kotlin を使って Suspending 関数を含むクラスのテストを書いていたところ、現状ではちょっとしたコツが必要なことが分かった。

以下の DownloadService#downloadFile() を呼び出す別のメソッドをテストするため、このインターネットと通信する downloadFile() メソッドをモックに置き換えようとした。

mock() を使った以下のコードは正常に実行可能。


class DownloadService {
    suspend fun downloadFile(url: String): File {
        // 省略
    }

    // 実際には別のメソッドも定義されている
}

@Test fun testDownloadFile() {
    val file = createTempFile()
    val target = mock<DownloadService>() {
        onBlocking{ downloadFile(any()) } doReturn file
    }
    runBlocking {
        val downloadedFile = target.downloadFile("http://example.com")
        assertEquals(file, downloadedFile)
    }
}

実際には DownloadService クラスに含まれる downloadFile() メソッド以外はモックを使わずにそのまま実行されて欲しかったので、mock()spy() に置き換え、このメソッドだけをモック実装する形に。

@Test fun testDownloadFile() {
    val file = createTempFile()
    val target = spy<DownloadService>() {
        onBlocking{ downloadFile(any()) } doReturn file
    }
    runBlocking {
        val downloadedFile = target.downloadFile("http://example.com")
        assertEquals(file, downloadedFile)
    }
}

ところが、このテストコードを実行すると NullPointerException がスローされてしまう(4 行目で実行される Stub 処理中に)。

mock() はいいけど spy() だとダメ。
しかしながら、このように spy() を使ってもモック対象(今回の例で言うと downloadFile() メソッド)が引数を取らない場合は、NPE がスローされることなくテストを実行できることが分かった。

試行錯誤した結果、spy() にラムダ式でモック処理を渡すのではなく、次のように doReturn().whenever().xxxx() のコードでモック処理を書けば正常にテストが実行できるようになった。

@Test fun testDownloadFile() = runBlocking {
    val file = createTempFile()
    val target = spy<DownloadService>()
    doReturn(file).whenever(target).downloadFile(any())
    val downloadedFile = target.downloadFile("http://example.com")
    assertEquals(file, downloadedFile)
}
7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4