配置build.gradle
testCompile "org.robolectric:robolectric:3.1.4"
配置Robolectric
在需要執行的測試類上,需要對Robolectric進行配置。可以把所有配置放到基類里面,其他測試類通過繼承的方式來共享配置
/**
* TestCase的基類,主要對使用到的Robolectric進行配置。
* <p>
* manifest:使用主代碼的manifest<br />
* packageName:如果build.gradle中配置了applicationId,此處需要制定包名<br />
* constants:配置常量<br />
* sdk:Robolectric目前不支持24,所以配置為23
* <p>
* Created by snow
* Date: 2016/12/2
* Time: 下午2:14
*/
@RunWith(RobolectricTestRunner.class)
@Config(manifest = "src/main/AndroidManifest.xml", packageName = "com.snow.demo", constants = BuildConfig.class, sdk = 23)
public class BaseTestCase {
}
支持MultiDex
Robolectrix對MultiDex支持不是很好,在執行Application.attachBaseContext()方法時,可能會拋出異常,因此需要進行捕獲處理
@Override
protected void attachBaseContext(Context base) {
/**
* Robolectric 對MultiDex的支持有問題,參考<a >issue</a>
*/
try {
super.attachBaseContext(base);
} catch (Exception multiDexException) {
// Work around Robolectric causing multi dex installation to fail, see
// https://code.google.com/p/android/issues/detail?id=82007.
boolean isUnderUnitTest;
try {
Class<?> robolectric = Class.forName("org.robolectric.Robolectric");
isUnderUnitTest = (robolectric != null);
} catch (ClassNotFoundException e) {
isUnderUnitTest = false;
}
if (!isUnderUnitTest) {
// Re-throw if this does not seem to be triggered by Robolectric.
throw multiDexException;
}
}
}
NoClassDefFoundError: javax/microedition/khronos/opengles/GL
在運行測試用例時,有可能會報上述異常,解決方法是在build.gradle中添加如下依賴:
testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
Robolectric中的Shadow概念
作用
模擬了Android系統中的真實對象,并對其中的一些方法做了hack。
名字
What's in a Name?
Why "Shadow?" Shadow objects are not quite Proxies, not quite Fakes, not quite Mocks or Stubs. Shadows are sometimes hidden, sometimes seen, and can lead you to the real object. At least we didn't call them "sheep", which we were considering.
code
Rocolectric的Shadows類中提供了很多的重載方法,提供所需的Shadow對象。
public static ShadowActivity shadowOf(Activity actual) {
return (ShadowActivity) ShadowExtractor.extract(actual);
}
public static ShadowActivityGroup shadowOf(ActivityGroup actual) {
return (ShadowActivityGroup) ShadowExtractor.extract(actual);
}
public static ShadowActivityManager shadowOf(ActivityManager actual) {
return (ShadowActivityManager) ShadowExtractor.extract(actual);
}
public static <T extends Adapter> ShadowAdapterView<T> shadowOf(AdapterView<T> actual) {
return (ShadowAdapterView<T>) ShadowExtractor.extract(actual);
}
public static ShadowAlarmManager shadowOf(AlarmManager actual) {
return (ShadowAlarmManager) ShadowExtractor.extract(actual);
}
public static ShadowAlertDialog shadowOf(AlertDialog actual) {
return (ShadowAlertDialog) ShadowExtractor.extract(actual);
}
ShadowIntent:
/**
* Shadow for {@link android.content.Intent}.
*/
@SuppressWarnings({"UnusedDeclaration"})
@Implements(Intent.class)
public class ShadowIntent {
@RealObject private Intent realIntent;
/**
* Non-Android accessor that returns the {@code Class} object set by
* {@link Intent#setClass(android.content.Context, Class)}
*
* @return the {@code Class} object set by
* {@link Intent#setClass(android.content.Context, Class)}
*/
public Class<?> getIntentClass() {
try {
return Class.forName(realIntent.getComponent().getClassName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}