iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 5
0

上一篇有用MVP模式提到Mock的概念,Mock是什麼呢?你有沒有發現我們在測試Presenter的時候除了被測試的程式,還有一些外部相依的物件需要被呼叫。在MainActivityPresenter的requestUserName()裡面除了Server()直接在function內部呼叫物件實體外,另外會用到的IView物件是由Constructor用Setter方式注入,如果我們在實體化Presenter的時候沒有帶入IView就會在執行時發生Nullpointer的錯誤。

正常情況在launch app時候、MainActivity執行時我們有把this指標放入Presenter裡,但是在單元測試裡沒有launch app去執行MainActivity那該怎麼把IView放入單元測試的Presenter中呢?

這時你可能會想到很直覺的解決方法

  • 直接實體化IView
  • 直接實體化MainActivity
        val view = IView()
        val presenter = MainActivityPresenter(view)

        //or
        val view = MainActivity()
        val presenter = MainActivityPresenter(view)

結果是大失所望,因為IView是Interface在Kotlin或是Java都無法直接實體化interface,那我們轉為實體化MainActivity一執行馬上發生錯誤,因為你沒有launch app無法執行MainActivity的生命週期。難不成我們的測試就做不下去了,當然不會,我們就測試目的Mock(模擬,仿造)一個有Activity的外殼又不是真的Activity的物件就行了,這要怎麼做呢?

class MockMainActivity : IView {
    override fun receivedUserName(name: String) {
        //only for mock, not implement ui here
    }
}

但為了讓大家熟悉Mock的觀念,我們先不要靠Library自己來手動實作Mock功能,
我們可以寫一個MockMainActivity的class一樣實作IView介面,然後receivedUserName留空,請記得我們的測試只是要驗證後receivedUserName()要被呼叫到一次。

class ExampleUnitTest {
    @Test
    fun testRequestUserName() {
        val view = MockMainActivity() //剛剛是用mockk<IView>
        val presenter = MainActivityPresenter(view)

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

        presenter.requestUserName()

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

回到我們在上一章MVP的測試程式,最後把原本使用Mockk的IView改成MockMainActivity注入Presenter就OK了,測試後跟使用mockk一樣Test passed,這樣我們就手動完成Mock一個MainActivity物件。這樣看起其實Mock觀念一點也不難,我們透過DIY也可以做出來,Mock就是模仿一個物件。但試想如果今天有上百上千個Mock object要寫,那你的Testing code要變的多可怕多難維護,每做一個Test case就多一堆測試專用的class。比較好的方法還是用專門的Mocking framework像是Mockito或是Mockk,透過reflection的方式在runtime產生測試物件這是比較好的選擇。

單元測試的名詞

  • Stub : 被仿照的物件在測試期間參與回傳值的處理以驗證被測試物件。
  • Mock : 被仿照的物件在測試期間還參與驗證的部份。
  • Dummy : 被仿照的物件在測試期間不參與實際行為,只用來滿足填入參數
  • Spy : 被仿照的物件在測試期間還保有原始物件的功能,只有部份function被仿照。

在單元測試裡其實有一些比較嚴謹的定義如,stub,mock,spy,dummy等等,但我們實際使用這些mocking framework來操作時並沒有這麼細分,以mockk為例它提供我們一個很強大的mock類別操作,端看你怎麼使用,你只用stub的功能它就是stub,你用到mock的功能它就是mock,因為萬變不離其宗就是不同程度的仿照而已。所以我這邊先不對每個定義做很詳細的解釋,因為我們實際的操作大部份都是mock,所以我的介紹就用mock來統稱這些行為(因為Mockk跟Mockito是也用mock來命名嘛)。

下一章我們會繼續講到Mockk的使用方法。


上一篇
[Day 4] 從MVP模式開始練習Unit test
下一篇
[Day 6] Mockk與Mock的入門觀念
系列文
從0開始,全方面自動化測試Android App30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言