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

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

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

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

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

調研與選型

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

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

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

一種常見的解決方式是經過架構來解決,將一些代碼邏輯和官方類解耦,好比MVP,對Presenter作測試,由於大部分邏輯都在Presenter,因此還ok。ide

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

而咱們的項目就是這樣的,全部的邏輯都在Activity中編寫,一旦測試邏輯就確定繞不開官方類。測試

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

一種是經過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爲基礎,完成單元測試的編寫。

相關文章
相關標籤/搜索