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

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

Joe Tsai
Joe Tsai
Sep 19, 2018 · 6 min read

前情提要

在 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 的基本使用方式:

Joe Blog

想到什麼寫什麼

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store