根據Espresso針對IdlingResource的介紹:
Represents a resource of an application under test which can cause asynchronous background work to happen during test execution (e.g. an intent service that processes a button click). By default, Espresso synchronizes all view operations with the UI thread as well as AsyncTasks; however, it has no way of doing so with “hand-made” resources. In such cases, test authors can register the custom resource via IdlingRegistry and Espresso will wait for the resource to become idle prior to executing a view operation.
簡言之,Espresso內建有偵測android必有的UI thread,以及AsyncTask所產生的thread的Idle狀態。但如果是自己產生的thread則無法被偵測,於是在原本的做法可能會用sleep()來等待,不過這是官方不建議的做法。
在官方文件內有列出幾個不建議的解法:
- 使用Thread.sleep():不只會拉長測試時間,也因需要延遲的時間無法預測,造成不同的機器依然會無法完成測試。
- 使用失敗時重跑的機制:就像while迴圈一樣,重複執行只會產生多餘的效能損耗。
- 使用CountDownLatch:使用thread來等待thread,會造成不必要的程式複雜度。
正確的解法應該是透過Espresso提供的IdlingResource相關的類別,或是透過繼承來實作專屬的IdlingResource,在使用註冊機制讓Espresso主動去檢查相關的resource。
詳細作法和注意事項請在官方文件查閱,這邊將以介紹IdlingResource的運作機制為主。
在自訂IdlingResource並使用時,必須要滿足以下兩個條件:
- 註冊自訂IdlingResource。
- 確認Idle狀態的機制。
註冊自訂IdlingResource
在測試的步驟執行前,先使用IdlingRegistry.getInstance().register來將自訂的IdlingResource註冊。
在執行如onView的函式時,會執行IdlingResourceRegistry.sync(),並將所有註冊過的IdlingResource傳入。
// In IdlingResourceRegistry |
如果先前已經註冊的IdlingResource並沒有存在於新的IdlingResource列表中的話,就會被取消註冊。然後重新在註冊新的這些。
// In IdlingResourceRegistry |
這邊再將IdlingResource一個個包裝成IdlingState,然後呼叫registerSelf()
// In IdlingResourceRegistry.IdlingState |
呼叫到registerIdleTransitionCallback(),這是在繼承IdlingResource時,需要實作的一個函式。
// OkHttp3IdlingResource |
如此自訂的IdlingResource可以在這取得對應的IdlingState,能用來與IdlingResourceRegistry溝通。
IdlingResourceRegistry是Espresso用來監控所有自訂的IdlingResource的類別。
確認Idle狀態的機制
IdlingResourceRegistry確認IdlingResource狀態的方式,有分被動和主動兩種:
主動
IdlingResourceRegistry會在必要時輪詢所有已註冊的IdlingResource:
// In IdlingResourceRegistry |
因此每個IdlingResource都會被要求實作isIdleNow(),給予當前IdlingResource是否Idle的自我診斷。
被動
透過registerIdleTransitionCallback取得的IdlingState來呼叫onTransitionToIdle()可以通知IdlingResourceRegistry當前的IdlingResource已經結束任務:
// In IdlingResourceRegistry |
其作法是發出一個message,然後handler接到message後會呼叫到handleResourceIdled(),裡面會再輪詢一次所有IdlingResource的狀態,如此就可以在IdlingResource變Idle時,即時更新所有IdlingResource的狀態。
主動+被動
以OkHttp3IdlingResource為例,是在建立時傳入HttpClient的Dispatcher。
Dispatcher也可以取得目前仍在執行的任務數量:
// In OkHttp3IdlingResource |
在Dispatcher加上Callback,就可在HttpClient完成操作時即時收到通知,並同時通知IdlingResourceRegistry:
// In OkHttp3IdlingResource |
總結
綜合以上分析,我們可以將IdlingResource的機制簡單分成以下幾個步驟:
- 透過
IdlingRegistry.getInstance().register註冊。 - 註冊後在
registerIdleTransitionCallback()取得IdlingResource對應的IdlingState,完成雙向連結。 - IdlingResourceRegistry主動呼叫
isIdleNow()來詢問,或是呼叫onTransitionToIdle()由IdlingResource主動告知已進入Idle狀態。