Android單元測試(十):內容更新篇

距離我上一次寫單元測試的內容已經是一年半以前了,在這期間有個別的內容隨着相關庫的升級也已經過時了。避免“誤人子弟”,我對之前的Demo進行一下升級,本文就是相關內容的記錄。

如果你對單元測試相關內容不瞭解,請先閱讀之前的九篇博客。本篇僅是對之前內容的更新。

1.Robolectric配置

首先升級Robolectric的依賴至最新:

testImplementation "org.robolectric:robolectric:4.3.1"

在根目錄的gradle.properties文件中添加:

android.enableUnitTestBinaryResources=true

之前的配置中,需要通過@Config註解指定constants = BuildConfig.class,也可指定sdk的版本信息:

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 23)
public class MainActivityTest {

}

在4.0開始的版本中,默認不需要指定constants = BuildConfig.classconstants配置也被移除,所以默認可以去除@Config的這行固定配置。

如果要指定sdk版本的話,可以在src/test/resources目錄下創建一個名爲robolectric.properties的文件在你所要生效的包名下。其中更深的包中的值覆蓋更淺的包中的值。當測試類或方法有@Config註釋,這些註釋覆蓋屬性文件中的任何配置。具體如下圖所示:

在這裏插入圖片描述
這樣相比之前的方式確實友好很多。詳細配置及其他用法可以參看官方文檔

2.Robolecture與AndroidX

隨着AndroidX的到來,Android官方也提供了相應的測試框架。

	testImplementation 'androidx.test:core:1.2.0'
    testImplementation 'androidx.fragment:fragment-testing:1.1.0'
    // AndroidJUnitRunner and JUnit Rules
    testImplementation 'androidx.test:runner:1.2.0'
    testImplementation 'androidx.test:rules:1.2.0'
    // Assertions
    testImplementation 'androidx.test.ext:junit:1.1.1'
    testImplementation 'androidx.test.ext:truth:1.2.0'
    testImplementation 'com.google.truth:truth:1.0'

Robolectric在4.0開始也支持了官方測試庫,並推薦使用它們。

變更有以下幾個方面:

  1. 使用AndroidJUnit4代替RobolectricTestRunner:
// 之前
@RunWith(RobolectricTestRunner.class)
public class MainActivityTest {

}

// 現在
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {

}
  1. 之前使用RuntimeEnvironment.application可以獲取到Application,現在推薦使用getApplicationContext方法獲取。
import androidx.test.core.app.ApplicationProvider.getApplicationContext

@Test
public void testResources() {
    Application application = getApplicationContext();
    String appName = application.getString(R.string.app_name);
    assertEquals("AndroidUT", appName);
}
  1. 獲取Activity方式調整。之前的方式是直接通過setupActivity或者buildActivity方法獲取:
MainActivity activity = Robolectric.setupActivity(MainActivity.class);
// 或者
ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class);
MainActivity activity = controller.get();

現在:

@RunWith(AndroidJUnit4.class)
public class MainActivityTest {

    @Rule
    public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class);

    private MainActivity mainActivity;

    @Before
    public void setUp(){

        mainActivity = activityRule.getActivity();
        // 或者
        ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class);
        scenario.moveToState(Lifecycle.State.CREATED);
        scenario.onActivity(activity -> {
            mainActivity = activity;
        });
    }

}

Fragment 獲取方式大同小異,不過要注意添加testImplementation 'androidx.fragment:fragment-testing:1.1.0'依賴。

@Test
public void testFragment() {
    FragmentScenario<SampleFragment> scenario = FragmentScenario.launch(SampleFragment.class);
    scenario.onFragment(fragment -> assertNotNull(fragment.getView()));
}

對於官方測試庫的詳細使用可以查看Android的官方文檔

3.AssertJ-Android

之前有提到AssertJ-Android這個庫,它是AssertJ的拓展,便於我們斷言Android View。

由於AndroidX的到來,這個庫也就不太適用於未來。官方也覺得他並不是一個好的方式,所以暫停了維護。推薦我們使用Google的truth。並且truth這個庫也內置在官方的測試框架裏面。它和AssertJ很相似,有興趣的可以瞭解一下。

這裏我暫時沒有使用truth,只是用 AssertJ的方式替換了 AssertJ-Android。僅僅是個人習慣問題。

之前的方式:

import static org.assertj.android.api.Assertions.assertThat;

 @Test
public void testView() {
    // Button是否可見
    assertThat(mJumpBtn).isVisible();
    // LinearLayout 方向,子View數量
    assertThat(mRoot)
         .isVertical()
         .hasChildCount(4);
    // CheckBox是否未選中
    assertThat(checkBox).isNotChecked();
}

修改後:

import static org.assertj.core.api.Assertions.assertThat;

@Test
public void testView() {
	// AssertJ-Android (已不在維護),這裏就是用普通方法實現
	// Button是否可見
	assertThat(mJumpBtn.getVisibility()).isEqualTo(View.VISIBLE);
    // LinearLayout 方向,子View數量
    assertThat(mRoot.getOrientation()).isEqualTo(LinearLayout.VERTICAL);
    assertThat(mRoot.getChildCount()).isEqualTo(4);
    // CheckBox是否未選中
    assertThat(checkBox.isChecked()).isEqualTo(false);
}

4.當前依賴版本

以下是當前項目所用到的測試框架的依賴版本:

	//junit
    testImplementation 'junit:junit:4.12'
	//mockito
    testImplementation "org.mockito:mockito-core:3.1.0"
    //powermock
    testImplementation "org.powermock:powermock-module-junit4:2.0.2"
    testImplementation "org.powermock:powermock-module-junit4-rule:2.0.2"
    testImplementation "org.powermock:powermock-api-mockito2:2.0.2"
    testImplementation "org.powermock:powermock-classloading-xstream:2.0.2"
    //robolectric
    testImplementation "org.robolectric:robolectric:4.3.1"
    //AssertJ
    testImplementation 'org.assertj:assertj-core:3.13.2'

	// AndroidX所需
    testImplementation 'androidx.test:core:1.2.0'
    testImplementation 'androidx.fragment:fragment-testing:1.1.0'
    // AndroidJUnitRunner and JUnit Rules
    testImplementation 'androidx.test:runner:1.2.0'
    testImplementation 'androidx.test:rules:1.2.0'
    // Assertions
    testImplementation 'androidx.test.ext:junit:1.1.1'
    testImplementation 'androidx.test.ext:truth:1.2.0'
    testImplementation 'com.google.truth:truth:1.0'

最後我將修改後的代碼也已經同步到了Github上,master分支是使用support 28.0依賴庫的,androidx部分的改動放在了androidx分支上,便於大家查閱。

內容暫時就這麼多。後面如果新的改動,我也會更新在這篇博客當中。


2020.01.16更新

更新gradle至3.x以後,原本的存放class文件目錄發生了改變。所以使用jacoco時,需要修改指定的類文件夾(檢查的覆蓋類)地址:

/intermediates/classes/debug
修改爲:
/intermediates/javac/debug/compileDebugJavaWithJavac/classes

在這裏插入圖片描述

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