Android 單元測試實戰(1)—— 調研與選型

Android 單元測試實戰(1)—— 調研與選型

單元測試搞了一段時間,發現網上很多關於單元測試的文章都是講了概念,講了框架的使用,但對於一個實際的項目的操作,因爲項目的複雜性,框架的穩定性等,確往往無法進行。本篇博客從實際出發,基於實際的項目總結而出。

本系列文章不會涉及到單元測試的概念,以及它的各種現實意義。僅從實現入手,關於它的優劣不做分析。

單元測試系列會分爲三篇博客:

  • Android單元測試調研與選型
  • 基於Powermock的Android單元測試常用方法指南
  • 基於Cobertra&sonarqube的單元測試覆蓋率統計

調研與選型

Google官方文提供了單元測試的支持。在創建項目的時候會默認創建testandroidTest目錄。分別是單元測試和集成測試。單元測試是對方法的測試,粒度較小,無需運行在真機上。集成測試需要每次運行需要跑在真機上,粒度較大,運行時間較長,而且不利於一些自動化的工作。

本系列的核心從單元測試入手,以自動化爲目的。

單元測試一個繞不開的話題就是對於android.jar的問題。由於android.*的官方類,在運行單元測試的時候,只有方法的聲明,內部的所有方法都會throw new RuntimeException('Stub')。那麼一旦有調用官方類的地方,比如ViewIntentActivity等,就會報錯,導致單元測試無法執行。

一種常見的解決方式是通過架構來解決,將一些代碼邏輯和官方類解耦,比如MVP,對Presenter做測試,因爲大部分邏輯都在Presenter,所以還ok。

但是不幸的,一般搞單元測試的時候,很難是從一個新項目入手的,架構很難變動,一旦修改架構,影響範圍比較廣。

而我們的項目就是這樣的,所有的邏輯都在Activity中編寫,一旦測試邏輯就肯定繞不開官方類。

官方文檔上提到了單元測試的兩種解決方案。

一種是通過Mock來解決,及將官方類的調用方法給代理一下,不會實際的調用官方類的相關方法。

另外一種是Robolectric,該框架通過在jvm上模擬android虛擬機,以單元測試的方式來完成集成測試。

Robolectric(放棄)

因爲最終沒有使用這個框架,所以先介紹一下這個框架。

該框架相當於是搭建了一個android虛擬機,其運行單元測試的時候,實質是運行了一個app。那麼其做測試的邏輯更傾向於appium等的UI測試,查詢一個控件,模擬點擊,驗證邏輯。

因爲其模擬的虛擬機,那麼他對官方的方法做了擴展,提供了一些列的ShadowXXX類,便於做驗證和模擬。比如獲取當前彈出的dialog,最後一個彈出的toast等等。

調研的時候,該框架最新版本爲4.3,並且從4.0開始,已經開始和官方的androidx.test下測試庫進行兼容。可以通過官方espresso完成一些列操作。那麼一套代碼,技能在控制檯運行,又能在模擬器上運行,想想還是挺美好的。

但是 !!!

因爲我們的項目都是在Activity裏面寫的,一些業務邏輯都是使用私有方法,那麼相對私有方法做驗證,通過查詢控件和通過UI的展示來驗證邏輯的正確與否,十分的複雜。

舉個例子:

@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.skip:
                startNextActivity(false);
                break;
            case R.id.splash_image:
                startNextActivity(true);
                break;
            default:
                break;
        }
    }
    
    private synchronized void startNextActivity(boolean isClick) {}

我想驗證不同的View點擊,調用的startNextActivity的參數是否正確。如果從UI上驗證十分的複雜,而且萬一startNextActivity的內部邏輯有問題,或者比較巧妙,那麼UI的驗證也不一定準確。

有方法解決嗎??肯定有,便是Mock,這個後面會說。

能夠Mock私有方法的常用的是Powermock,但是!!!!!

該框架和Robolectric存在着各種兼容問題,在我頭髮掉了一地之後,也沒有解決。

所以放棄了!!!!注意是放棄了Robolectric!!!!

Mock (使用)

mock是單元測試中常用的一種方式,通過對即將調用方法的修改,模擬調用方法的返回值等等,具體的概念百度上一大堆。我就不再這裏廢話了。

官方文檔上建議使用Mockito完成mock操作,但是該庫不支持靜態方法,私有方法,final等的mock

上面提到了Powermock,他提供更加強大的mock功能,而且它提供了Mockito的支持,使用上和Mockito基本上一樣。

比如說,對於上面的代碼,使用Powermock方法驗證邏輯如下:

    @Test
    public void onClickSkip() throws Exception {
        // mock activity, activity的所有方法都不會被執行
        LauncherActivity activity = PowerMockito.mock(LauncherActivity.class);
        // 指定activity的onClick不被`mock,調用真實的邏輯,以便進行單元測試
        PowerMockito.doCallRealMethod().when(activity, "onClick", ArgumentMatchers.any(View.class));

		 // Mock 官方類
        View view = PowerMockito.mock(View.class);
        // 指定getId的返回值
        PowerMockito.doReturn(R.id.skip).when(view, "getId");
		 // 調用測試的方法 
        activity.onClick(view);
     	 // 驗證指定的方法和參數是否被滴啊用	
        PowerMockito.verifyPrivate(activity).invoke("startNextActivity", false);
    }

代碼註釋很清楚,不在廢話。

按照上面的思路,其實可以驗證大部分的單元測試邏輯。

總結

綜上所述,決定使用Powermock爲基礎,完成單元測試的編寫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章