距離我上一次寫單元測試的內容已經是一年半以前了,在這期間有個別的內容隨着相關庫的升級也已經過時了。避免“誤人子弟”,我對之前的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.class
,constants
配置也被移除,所以默認可以去除@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開始也支持了官方測試庫,並推薦使用它們。
變更有以下幾個方面:
- 使用
AndroidJUnit4
代替RobolectricTestRunner
:
// 之前
@RunWith(RobolectricTestRunner.class)
public class MainActivityTest {
}
// 現在
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
}
- 之前使用
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);
}
- 獲取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