iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 6
0

在Day 5裡我們介紹了mock的一些基本觀念及DIY實作,但是真實世界不會有人都用DIY的方式來寫mock object,在mocking framework的幫助下我們可以很方便的實作我們的測試mock object。那mockk又是何方神聖呢?
早在Mockk出現前在JVM平台上比較受歡迎的單元測試框架是Mockito,它帶有強大的mock功能,可以仿造許多自定object的行為而不用像我在Day 5 Mock object中的文章提到,要改變mock object的細部行為還得自己寫mocking class,但Mockito在Android使用上有很多限制不再贅述,在Android + Kotlin的開發上Mockk改善了許多Mockito的缺點比較適合使用。

Mockk提供了很方便的映射功能,可以完全仿照物件的行為,讓你在測試時候可以快速製造物件內function或field的各種回傳值或行為,以我們在MVP章節為例,

class ExampleUnitTest {
    @Test
    fun testRequestUserName() {
        val view = mockk<IView>()
        val presenter = MainActivityPresenter(view)

        every {
            view.receivedUserName(any())
        } just Runs

        presenter.requestUserName()

        verify {
            view.receivedUserName(any())
        }
    }
}

Mock物件

我們看到了第一個跟mockk有關的部份是mockk<IView>()。

        val view = mockk<IView>()

這是mockk用映射的方式來覆寫IView裡的狀態,可以動態指定IView裡的function或是field回傳值,但這個時間點我們還沒有假造任何狀態,只是把有能力假造IView狀態的這個物件指給view這個變數。

every區塊

        every {
            view.receivedUserName(any())
        } just Runs

every指的是我們要假造IView的狀態,一旦遇到區塊裡的情況我們要做什麼事。我們的程式碼要假造的狀況是當每次IView被呼叫receivedUserName()的時候。但我們只是要驗證receivedUserName()有被呼叫所以在區塊後面接上
just Runs,這是告訴mockk我們只要紀錄receivedUserName被呼叫的狀態而不用管它的回傳值。any()是mockk裡的dummy物件,意指給一個任何型別的空物件,因為我們的測試不在意傳入的參數,function參數跟我們的測試無關只是為了符合interface的規定而已。

第一次接觸mock的人會覺得很莫名其妙,我們把IView mock之後的實體view放入presenter裡,presenter本來就會呼叫內部程式,view.recivedUserName()本來就會被呼叫呀!我今天在這裡用every區塊定義它的原因是什麼?有兩個原因。

  • 首先被mock的物件本身的細節是不存在的,我們只會需要知道mock物件的field或是function名稱然後改成我們希望的樣子,因此物件內的function或是field都是要被重新定義,我們關心的不是mock物件的實作,mock物件裡面的實作細節有無也不重要,我們關心的是mock物件一旦被呼叫時的回傳值與被測物件的互動。我們的例子沒有回傳值所以只呼叫just Runs告知mockk這個function被呼叫後就回傳void。假設有String回傳值的話應該會類似下面範例,也就是不管真的view.receivedUserName()裡面怎麼寫,我在測試時就是要它回傳Daniel Chen。
        every {
            view.receivedUserName(any())
        } returns ("Daniel Chen")
  • 再者利用呼叫every的方式,我們可以告訴mockk去紀錄mock物件的每一個動作,像這種沒有回傳值的狀況我們只能得知function被呼叫,而不是function回傳什麼,所以才能在驗證的時候去比對mock物件是不是真的有做了我們預期行為(被呼叫一次)。

verify區塊

        verify {
            view.receivedUserName(any())
        }

verify顧名思義就是驗證,用來驗證我們的被測程式(presenter)在執行時,mock物件是否如我們的預期一樣有做verify區塊內的行為。verify區塊不是必要的,如果你的驗證沒有mock物件參與或跟mock物件無關時,可能只需要用AssertEqual之類的值比對方式來做驗證,例如[Day 2]舉例的add function並不需要驗證mock物件,那自然也不用呼叫verify。但如果今天你是測試Android的MVP時那verify是一定會用到的東西。

回到[Day 4]MVP的章節,我們預期的是當presenter被呼叫requestUserName()的時候presenter裡面的view.receivedUserName()這行程式會被呼叫到。所以當我們真的去執行presenter時,如果執行到verify的區塊時,view.receivedUserName()被呼叫到測試程式就會通過,沒有呼叫到測試程式就會失敗。

我們先簡單的介紹我們在MVP的範例用到的mockk範例與mocking framework的使用觀念,下一節會繼續介紹mockk的一些常用的進階用法。


上一篇
[Day 5] DIY寫一個Mock object
下一篇
[Day 7] 解決常見的單元測試難題 - Static
系列文
從0開始,全方面自動化測試Android App30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言