MockK:一款強大的 Kotlin Mocking Library (Part 1 / 4)

用 Kotlin + Mockito 寫單元測試會碰到什麼問題?

前情提要

在 Java 的世界裡面,說到單元測試,不得不提到 Mockito,它是目前 Java 最多人使用的 Mocking framework,但若來到 Kotlin 的世界呢?我們還能仰賴 Mockito 進行單元測試嗎?有沒有專門為 Kotlin 這款語言量身打造的 Mocking framework 呢?

這系列文章會手把手教你如何使用 MockK,讓你了解它好用的地方在哪裡?以及它解決了什麼樣的問題?一共分成 4 篇文章,包括以下內容:

  1. 用 Kotlin + Mockito 寫單元測試會碰到什麼問題?(這篇)
  2. MockK 功能介紹:mockk, every, Annotation, verify
  3. MockK 功能介紹:Relaxed Mocks, 再談 Verify , Capture
  4. 如何測試 Static Method, Singleton

Kotlin + Mockito 會碰到的問題

首先,我們來看看 Kotlin 跟 Mockito 一起使用時,最常碰到的幾個問題:

Mockito cannot mock/spy because : — final class

當你嘗試在 Kotlin 中 Mock 一個 Class 的時候,你會得到這樣的錯誤:

org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class com.joetsai.kotlinunittest.token.TokenRepository
Mockito cannot mock/spy because :
— final class

這是因為在 Kotlin 裡面任何 Class 預設都是 final 的,而且 Mockito 預設情況是不能 Mock fincal Class 的,要解決這個問題有兩種方式:

  1. Open Class
    在你要 Mock 的 Class 前面加上 open,這個錯誤就會消除了,但假設今天你有 100 個 Class 要 Mock,那你必須要 Open 100 個 Class,而且 open 只為了讓你可以使用 Mockito,是有點治標不治本的感覺。
  2. 開啟隱藏版 mock final 功能
    前面有提到 Mockito 預設情況是不能 Mock final Class 的,但是我們可以把這個功能打開,詳細作法可以參考這篇文章:

如此一來,就可以正常的 Mock 一個 Class 囉!

java.lang.IllegalStateException: anyObject() must not be null

碰到這個問題,主要是因為你有使用到像 any(), eq(), argumentCaptor(), capture() 這類的方法,由於 Mockito 在呼叫這些方法是有可能回傳 Null 的,而 Kotlin 預設情況是無法接受 Null 的,所以你需要做個轉換,讓你的方法可以接受 Null 值:

程式碼出處:MockitoKotlinHelpers.kt

when 要加上反引號才能使用

在 Kotlin 裡面 when是保留字,它的功能就像是 Java 裡的 Switch,因此如果你要使用 Mockito 的 when,你必須加上反引號才能使用:

`when`(xxxx).thenReturn(xxx)

加上反引號後看起來很不舒服,要解決這個問題的話,可以捨棄 Mockito 改用 mockito-kotlin 這款套件:

它把 `when` 改成 whenever 來使用,同時排除了 java.lang.IllegalStateException: anyObject() must not be null 的問題,此外它提供了更簡潔的寫法,有興趣的可以到它的 Github 上看看,不過 mockito-kotlin 的底層依舊是呼叫 Mockito API,所以 Mockito 做不到的事情,這款套件也做不到的。

測試靜態方法(Static Method)

萬一你想要測試靜態方法呢?Mockito 目前沒有支援這項功能, mockito-kotlin 當然也沒有支援,直覺你會聯想到 PowerMock,不過 PowerMock 在使用上非常不直覺,且初始化動作繁瑣,官方文件 也點出目前現有的問題:

Note that Mockito2 support is currently experimental

PowerMock version 1.7.0 and upper has experimental support of Mockito 2.

A lot of issues are not resolved still. PowerMock uses internal Mockito API, but at least it possible to use both mocking framework together.

另外,這兩款套件是不同團隊開發的,PowerMock 的更新速度遠遠比不上 Mockito,目前仍有兼容性上的問題:

撰寫本文時 Mockito 已經出到 2.21 版,PowerMock 官網上卻沒有提到對應的版本是多少…

我自己用 PoweMock 的經驗是即便我照版本對照表使用,還是會碰到一堆問題,解決了一個問題又會蹦出另外一個問題,有點拆東牆補西牆的感覺,折騰了許久決定另謀出路。

下一篇將會介紹 MockK 的基本使用方式: