MockK 的兩三事
單元測試 (unit test) 已是許多初具規模的開發部門要求落實的一項重要開發流程,也是一項專案要進入正式發布階段的一個最低門檻。
如果你對單元測試已經非常熟悉,你可以跳過這篇文章直接去 MockK 官網學習如何使用 MockK。
跟 MockK 官網不同的地方是,這篇文章會以模擬真實案例的方式,讓你了解如何用 Kotlin + MockK 來撰寫基本的單元測試。
為什麼單元測試很重要,沒有會怎麼樣?
狗狗魯克公司生產了一台家用機器人,號稱能夠包辦所有家事,唯一的缺點是預設音量過小,「這音量叫不醒我啊!」客戶不看操作手冊就對客服人員咆哮著,於是專案管理員找到了工程師克里斯,要求他調高預設音量,克里斯剛跟同事吵過架,非常不爽的就把預設的 50 分貝隨便加個 0,以預設 500 分貝的數值發布了新的韌體,這客戶開心的更新了韌體之後,就再也沒抱怨過了…
當然事實上揚聲器是不可能發出 500 分貝的聲音,但 — 沒經過單元測試的程式碼對於客戶來說就是如此可怕。
好可怕,那我們趕快寫單元測試吧
在寫單元測試之前,先介紹一個術語叫做 mock,顧名思義就是模擬/模仿的意思,通常我們寫測試時會 mock 一個變因,用來測試物件因為這個變因而實際造成的結果,例如現在我們抓到一隻狼人,想測試他看到滿月會不會變身,所以我們 mock 了一個月亮,再 mock 這月亮的狀態為滿月,將這顆月亮擺在狼人面前,再觀察這狼人,OK 變身了,passed!
扯遠了,我們說回出包的克里斯
這位工程師克里斯非常懊悔,決定為這台機器人的韌體寫單元測試,這台機器人 Robot 配置了一個 Power 用來充電,每次充電提供 50 毫安培的電量,簡易的程式碼如下:
實際使用 MockK 來寫測試
要測試 Robot 的各項功能,是否正確的使用 Power,所以我們 mock 一個 Power 出來,配置在新建立的 Robot 上面
relaxed 設定為 true,即所有 method 都不需要自定義
relaxUnitFun 設定為 true,即所有回傳 unit 的 method 都不需要自定義,只需要定義有回傳值的 method
使用 every 來模擬行為
測試 Robot 的 charge()
功能,是否正確使用 power.provide()
,所以先針對 Power 能提供的電源做修改
驗證流程
假設加入了 Power 的保護機制,為了維持 Power 零件壽命,Robot 每次charge
都會先讓 Power prepare
熱機,再執行 provide
供電,最後執行 complete
做安全斷電。
我們再加入一個 Battery 來取代原本過於簡化的 energy 跟 Power 配合,新的程式碼如下:
使用 verify 驗證函式是否確實執行
確保 Robot 在 charge 時,有執行到預期的 Power 功能,exactly 參數可以設定以檢驗確實的執行次數
使用 slot & capture 驗證傳入參數
如果我們想驗證每次 Robot charge 時,Power 提供給 Battery 的電量參數是否如預期
這邊用了一個 Int 形態的 slot 來讓 capture 填入捕捉到的數值,Power 預設的 provide 供電是 10,所以驗證最後的 slot.captured 應該要是 10
使用 mockkStatic 和 mockkObject
好,現在有個功能完整的 Robot 了(才怪),我想要委託一個 Simple Factory 來生產 Robot,但這個 free style 的工廠會隨機產出不同國家的 Robot,每個國家的 Robot 打招呼方式不同,該怎麼測試他真的產出我想要的 Robot 呢?
我們把 Robot 增加一個 function hello,再新增一個 TaiwanRobot 繼承 Robot,這樣一來我的 TaiwanRobot 就會用中文打招呼了,然後我再新增一個 FreeStyleRobotFactory,新的程式碼如下:
FreeStyleRobotFactory.createRobot()
使用了 RegionObjectUtils.getRandomRegion()
來亂數取得國家,再用對應的國家產生對應的機器人,因為亂數取得的國家是變因,但要 mock 它時會發現無法用 mockk<?> 的方式來模擬 RegionObjectUtils.getRandomRegion
,因為他是一個 object,所以要用 mockkObject
或 mockkStatic
的方式來 mock
使用 mockkConstructor 來模擬 constructor
假設現在連 TaiwanRobot 的打招呼方式都 free style 了,我該怎麼測試 createRobot 呢?
先實作到這裡,一個簡單的 Robot 單元測試就完成了(咦?一開始說好的預設音量的測試呢?)
最後
本篇僅介紹 MockK 的基礎用法,MockK 還有很多其他非常易於寫測試的功能,例如 every/verify
搭配 and(), or(), less(), more(), etc.
的用法,或是例如 private functions mocking 的方法,測試 Kotlin Coroutines 的方法,等其他各種技巧可以參見官方網站的說明:https://mockk.io/
Enjoy!
參考連結
https://mockk.io/
https://medium.com/joe-tsai/mockk-%E4%B8%80%E6%AC%BE%E5%BC%B7%E5%A4%A7%E7%9A%84-kotlin-mocking-library-part-1-4-39a85e42b8