配置build.gradle
testCompile "org.robolectric:robolectric:3.1.4"
配置Robolectric
在需要執(zhí)行的測(cè)試類上,需要對(duì)Robolectric進(jìn)行配置垃沦】透可以把所有配置放到基類里面,其他測(cè)試類通過繼承的方式來共享配置
/**
* TestCase的基類肢簿,主要對(duì)使用到的Robolectric進(jìn)行配置靶剑。
* <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對(duì)MultiDex支持不是很好桩引,在執(zhí)行Application.attachBaseContext()方法時(shí),可能會(huì)拋出異常收夸,因此需要進(jìn)行捕獲處理
@Override
protected void attachBaseContext(Context base) {
/**
* Robolectric 對(duì)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
在運(yùn)行測(cè)試用例時(shí),有可能會(huì)報(bào)上述異常卧惜,解決方法是在build.gradle中添加如下依賴:
testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
Robolectric中的Shadow概念
作用
模擬了Android系統(tǒng)中的真實(shí)對(duì)象厘灼,并對(duì)其中的一些方法做了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對(duì)象手幢。
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);
}
}
}