Android單元測試編寫二三事

0. 環境

  • Mac OS 10.13.4
  • Android Studio 3.1.2
  • Junit 4.12
  • Robolectric 3.7.1
  • Mockito 2.13.0
  • PowerMock 2.0.0-beta.5

1. 預備知識

1.1 JUnit4

  1. 什麼是 JUnit4

1.2 Robolectric

  1. 什麼是 Robolectric

1.3 Mockito

  1. 什麼是 Mockito

1.4 PowerMock

  1. 什麼是 PowerMock
  2. PowerMock 簡介

2. 配置 Robolectric

單元測試主要基於 JUnit 和 Robolectric 進行。Android Studio 默認集成好了 JUnit,而 Robolectric 則須要稍稍配置一下,這裏提供兩種方式進行配置。html

2.1 給每一個測試類單獨配置

在每一個測試類上都加上註解java

@RunWith(RobolectricTestRunner.class)
@Config(application = TestMyApplication.class, constants = BuildConfig.class, manifest = "./AndroidManifest.xml",packageName = "com.your.package")
複製代碼

其中,TestMyApplication是測試代碼的初始化Application,能夠把App的Application的邏輯放在這裏面android

2.2 在 TestMyRobolectricRunner 中集中配置

  1. 自定義 TestMyRobolectricRunner,繼承自 RobolectricTestRunner
  2. 自定義 TestMyApplication,這個 TestMyApplication 承擔的做用是初始化一些東西,好比 SDK 等,和 App 中的 Application 做用是同樣的
  3. 在 TestMyRobolectricRunner 中重寫 buildGlobalConfig 方法
  4. 在測試類中,就能夠再也不使用 @RunWith(RobolectricTestRunner.class) 註解,轉而使用 @RunWith(TestMyRobolectricRunner.class) 註解
public class TestMyRobolectricRunner extends RobolectricTestRunner {

    /** * Creates a runner to run {@code testClass}. Looks in your working directory for your AndroidManifest.xml file * and res directory by default. Use the {@link Config} annotation to configure. * * @param testClass the test class to be run * @throws InitializationError if junit says so */
    public TestMyRobolectricRunner(Class<?> testClass) throws InitializationError {
        super(testClass);
    }

    @Override
    protected Config buildGlobalConfig() {
        return new Config.Builder()
                .setApplication(TestMyApplication.class)
                .setConstants(BuildConfig.class)
                .setManifest("AndroidManifest.xml")
                .setPackageName("com.yongf.mypackagename")
                .build();
    }
}
複製代碼

3. Android 單測中的一些 case

3.1 如何測試 SharedPreference

SharedPreferences preferences = RuntimeEnvironment.application.getSharedPreferences(PREFERENCE_KEY, Context.MODE_PRIVATE);
複製代碼

3.2 如何測試Intent跳轉

好比,點擊一個按鈕之後在知足條件的狀況下,會跳轉到一個新頁面。怎麼測呢,能夠先知足各類設置條件,而後模擬按鈕點擊,而後獲取系統跳轉的下一個 activity 是否指定的 activity。git

//按鈕點擊後跳轉到下一個Activity
forwardBtn.performClick();
Intent expectedIntent = new Intent(sampleActivity, LoginActivity.class);
Intent actualIntent = ShadowApplication.getInstance().getNextStartedActivity();
assertEquals(expectedIntent, actualIntent);
複製代碼

3.3 如何測試Dialog的顯示

某些狀況下,彈出Dialog。github

ShadowDialog latestDialog = ShadowApplication.getInstance().getLatestDialog();
複製代碼

遺憾的是,目前只能測試是否有對話框彈出,並不能測試對話框的內容(PS: 若是觀衆朋友你會的話,千萬別吝嗇你的才華,請在評論中或者 issue 中賜教~)app

3.4 如何測試Toast的顯示

某些狀況下,彈出 Toastide

Toast latestToast = ShadowToast.getLatestToast();
String textOfLatestToast = ShadowToast.getTextOfLatestToast();
複製代碼

能夠測試 Toast 的顯示,以及 Toast 的內容單元測試

3.5 如何設置測試用例的Application

參考上述 配置 Robolectric 章節的示例代碼測試

3.6 如何指定單個測試[類|方法]的sdk版本

單個測試類,在[類|方法]上面添加 @Config(sdk = 21) 便可,具體的 sdk 版本本身指定ui

3.7 須要用戶權限受權的場景如何測試

目前個人作法是指定 sdk 版本爲21如下,由於沒有動態權限,這樣須要用戶受權的部分就能走。但這樣帶來的問題就是,權限未受權的分支部分代碼沒有被覆蓋到。

若是你有更好的作法,趕忙來告訴我吧,謝謝啦~

3.8 如何測試 onActivityResult

  1. 使用 Robolectric 構造一個 activity
  2. 而後直接調用 onActivityResult 方法

3.9 若是出現 not mocked,怎麼辦?

在運行的測試用例的時候,你可能會遇到以下錯誤:

java.lang.RuntimeException: Method isEmpty in android.text.TextUtils not mocked. See http://g.co/androidstudio/not-mocked for details.
at android.text.TextUtils.isEmpty(TextUtils.java)
at com.example.robolectric.TextUtilsTest.testIsEmpty(TextUtilsTest.java:14)
複製代碼

如何解決呢?很簡單,把 Android 源碼中這個類搬到測試代碼目錄下便可(注意要保持包名一致)

4. 單元測試編寫實戰

TODO

4.1 搭建環境

  1. 新建 demo 項目
  2. 引入相關依賴
  3. TODO
相關文章
相關標籤/搜索