iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 11
1

昨天的部份已經介紹過MVVM的範例了,也提到了MVVM模式設計的好處,針對昨天的範例我們來實作一下單元測試。

class ExampleUnitTest {

    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()

    @Test
    fun testMvvmGetUser() {
        //given
        val serverApi = mockk<ServerApi>()
        val viewModel = MyViewModel()
        viewModel.apiManager = serverApi
        val userList = listOf(
            User("Daniel", "Chen"),
            User("Dennis", "Wang"), User("Dan", "Lee")
        )
        every { serverApi.requestUser() }.returns(userList)

        //when
        viewModel.getUsers()

        //then
        assertEquals(1, viewModel.getUsers().value?.size)
    }
}

針對昨天的MVVM的範例我們怎麼寫unit test呢?
首先我們定義要測試的目標function是ViewModel.getUsers(),也就是要驗證ViewModel裡的observer users資料處理是否正確,因為observer users會通知Activity去update UI,有了清楚的目標後我們就可以開始寫單元測試了。

我們一樣在ExampleUnitTest這個Class去實作我們的unit test,我們把這個測試取一個名字testMvvmGetUser(),在實作前我們要注意要先宣告一個測試規則InstantTaskExecutorRule(),這個目的是要Junit runner可以在unit test中立刻回傳LiveData的變數而不是真的要去呼叫Activity裡的users觀察者。

這個範例我們把ViewModel裡面必要的變數都mock起來,其中最重要的變數就是ServerApi,跟MVP範例中提到的單元測試一樣,我們並不需要真的去call Server,因為Server response的是阿貓阿狗不是重點,因為我們要測試的是Production code的流程是否如我們預期的邏輯回傳,而回傳的data要在Activity怎麼被畫出來也不是重點因為我們只要確認ViewModel給Activity的data是對的就好了。

當我們把ServerApi mock起來後我們就可以動態給予測試資料。這時我們就假造一個userList,然後利用every區塊告知Junit runner只要呼叫reqeustUser()就回覆這個預設的userList。這個userList有三筆data,在viewModel.getUser()執行後只會跑出Daniel Chen這筆資料,最後再用assertEquals的方法把我們預期的回傳的list size = 1跟實際的ViewModel跑出來的list size去做比對。你應該有發現跟測試MVP跟MVVM的不同之處,MVP裡我們驗證的是View的interface跟Presenter的互動狀態而在MVVM我們驗證的是ViewModel裡observer本身的狀態改變。

結論

完成了MVP與MVVM的範例後,一定有人會想說好像有沒有做單元測試都沒差,因為測試的邏輯很容易一眼就看出來。其一是範例中的流程比較簡單,用太複雜的範例就變成是在介紹程式邏輯大家應該會直接按上一頁,其二是單元測試目標就是測試一個小單元(function)也就是單一邏輯,本來就不應該太複雜。如果你寫起來發現單元測試很難寫的話,那你就必須考慮是否這個function寫的太多邏輯或是耦合太多相依類別,這時我會建議你該review code並考慮重構。

如果上述結論無法說服你的話,再試想看看如果今天需要測試的function有成千上萬你要怎麼在每次的release前都確定沒問題呢?除非這個codebase只有你一個人在寫,你也確定不會有第三人去動到邏輯的更動。但如果有其他人改了你的code而reviwer也不是你就可能會造成問題,另一方面如果你今天想要對三個月前的程式做修改或是重構refactoring,你可能很順手的就改好了卻忘了當初的一個邏輯要保留,例如我們MVVM的範例中要filter特定字串,可能不會當場有crash issue但是卻造成資料流的side-effect,直到QA的手上才發現UI顯示不對。如果被QA發現後再去修Bug,這樣就可能會大大的delay release時間,因為你還要先去trace code然後再回想當初的規格是寫什麼,多想一分鐘你的時間成本就多浪費一分鐘。

比較好的開發流程應該是工程師在開發當下就發現問題解決問題,這樣的時間成本是最低的。一個是依照QA report再去回想驗證,另一個是利用程式自動找程式的問題,兩者效率不言可諭。所以確保這個流程最好的做法就是動手寫unit test,unit test會記錄當初task的spec能夠讓你事後回想當初的use case好處多多。

明天我們再來談談MVP跟MVVM你要選擇哪個開發。


上一篇
[Day 10] MVVM與單元測試
下一篇
[Day 12] 單元測試的選擇 MVP vs MVVM
系列文
從0開始,全方面自動化測試Android App30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言