Android 自定义阴影对象在Robolectric中究竟是如何工作的?
如果我为我的活动编写一个自定义卷影,并将其注册到RobolectrictTestRunner,那么无论何时启动,框架都会用我的自定义卷影拦截该活动吗Android 自定义阴影对象在Robolectric中究竟是如何工作的?,android,unit-testing,robolectric,Android,Unit Testing,Robolectric,如果我为我的活动编写一个自定义卷影,并将其注册到RobolectrictTestRunner,那么无论何时启动,框架都会用我的自定义卷影拦截该活动吗 谢谢。简短的回答是不 Robolectric对截获的类别和仪器有选择性。在撰写本文时,将插入指令的唯一类必须具有与以下选择器之一匹配的完全限定类名: android.* com.google.android.maps.* org.apache.http.impl.client.DefaultRequestDirector Robolectri
谢谢。简短的回答是不 Robolectric对截获的类别和仪器有选择性。在撰写本文时,将插入指令的唯一类必须具有与以下选择器之一匹配的完全限定类名:
android.*
com.google.android.maps.*
org.apache.http.impl.client.DefaultRequestDirector
Robolectric存在的全部原因是Android SDK jar中提供的类在JVM中调用时抛出异常(即不是在模拟器或设备上)。您的应用程序活动的源不是“恶意的”(调用方法或构造函数时,它可能不会引发异常)。Robolectric的目的是允许您对应用程序的代码进行测试,否则,由于SDK的编写方式,这是不可能的。创建Robolectric的其他原因包括:
- SDK并不总是具有允许您查询由应用程序代码操纵的Android对象状态的方法。可以写入阴影以提供对此状态的访问
- Android SDK中的许多类和方法都是final和/或private或protected,因此很难创建应用程序代码所需的依赖项,否则这些依赖项将可用于应用程序代码
为什么要对活动进行阴影处理?作为更新,我已经能够为自己的类创建阴影,只要在任何可能的加载程序对该类执行操作之前小心地绑定阴影类。因此,根据说明,在机器人运行器中,我做到了:
@Override protected void bindShadowClasses() {
Robolectric.bindShadowClass(ShadowLog.class);
Robolectric.bindShadowClass(ShadowFlashPlayerFinder.class);
}
我有没有提到我有点作弊?上面的原始答案(当然)是正确的。所以我在我真正的课堂上用这个:
package android.niftyco;
public class FlashPlayerFinder {
.. .
我的模拟(阴影)在我的测试包的后面,正如人们所期望的:
package com.niftyco.android.test;
@Implements(FlashPlayerFinder.class)
public class ShadowFlashPlayerFinder {
@RealObject private FlashPlayerFinder realFPF;
public void __constructor(Context c) {
//note the construction
}
@Implementation
public boolean isFlashInstalled() {
System.out.print("Let's pretend that Flash is installed\n");
return(true);
}
}
是的,如果您将RobolectrictTestRunner子类化,请向构造函数添加一个自定义包,并在bindShadowClasses方法中加载阴影类。无需使用android。*软件包技巧 (注:这是robolectric-1.1) RobolectrictTestRunner#setupApplicationState中提供了许多钩子,您可以覆盖这些钩子 下面是我对RobolectrictTestRunner的实现
import org.junit.runners.model.InitializationError;
import com.android.testFramework.shadows.ShadowLoggerConfig;
import com.xtremelabs.robolectric.Robolectric;
import com.xtremelabs.robolectric.RobolectricTestRunner;
public class RoboRunner extends RobolectricTestRunner {
public RoboRunner(Class<?> clazz) throws InitializationError {
super(clazz);
addClassOrPackageToInstrument("package.you're.creating.shadows.of");
}
@Override
protected void bindShadowClasses() {
super.bindShadowClasses(); // as you can see below, you really don't need this
Robolectric.bindShadowClass(ShadowClass.class);
}
这在Robolectric 2中发生了显著变化。您可以指定而不是编写自己的TestRunner
import org.junit.runners.model.InitializationError;
import com.android.testFramework.shadows.ShadowLoggerConfig;
import com.xtremelabs.robolectric.Robolectric;
import com.xtremelabs.robolectric.RobolectricTestRunner;
public class RoboRunner extends RobolectricTestRunner {
public RoboRunner(Class<?> clazz) throws InitializationError {
super(clazz);
addClassOrPackageToInstrument("package.you're.creating.shadows.of");
}
@Override
protected void bindShadowClasses() {
super.bindShadowClasses(); // as you can see below, you really don't need this
Robolectric.bindShadowClass(ShadowClass.class);
}
例如:
@Config(shadows = {ShadowAudioManager.class, ShadowContextWrapper.class})
可能会很晚,但是从这里:org.roblectric.bytecode.Setup,您可能会发现关于插入哪些类的更多细节
public boolean shouldInstrument(ClassInfo classInfo) {
if (classInfo.isInterface() || classInfo.isAnnotation() || classInfo.hasAnnotation(DoNotInstrument.class)) {
return false;
}
// allow explicit control with @Instrument, mostly for tests
return classInfo.hasAnnotation(Instrument.class) || isFromAndroidSdk(classInfo);
}
public boolean isFromAndroidSdk(ClassInfo classInfo) {
String className = classInfo.getName();
return className.startsWith("android.")
|| className.startsWith("libcore.")
|| className.startsWith("dalvik.")
|| className.startsWith("com.android.internal.")
|| className.startsWith("com.google.android.maps.")
|| className.startsWith("com.google.android.gms.")
|| className.startsWith("dalvik.system.")
|| className.startsWith("org.apache.http.impl.client.DefaultRequestDirector");
}
谢谢你的解释。我想隐藏我的活动的原因是,它是由我的应用程序通过调用
startActivityForResult(…)
启动的。我有以下代码:ShadowActivity ShadowActivity=shadowOf(activityA);Intent startedIntent=shadowActivity.getNextStartedActivity();ShadowIntent ShadowIntent=shadowOf(startedIntent);资产(shadowIntent.getComponent().getClassName(),equalTo(activityB.class.getName())代码>我想从activityB获取视图。使用本机API,我使用了一个ActivityMonitor
,但我想知道如何使用Robolectric。
public boolean shouldInstrument(ClassInfo classInfo) {
if (classInfo.isInterface() || classInfo.isAnnotation() || classInfo.hasAnnotation(DoNotInstrument.class)) {
return false;
}
// allow explicit control with @Instrument, mostly for tests
return classInfo.hasAnnotation(Instrument.class) || isFromAndroidSdk(classInfo);
}
public boolean isFromAndroidSdk(ClassInfo classInfo) {
String className = classInfo.getName();
return className.startsWith("android.")
|| className.startsWith("libcore.")
|| className.startsWith("dalvik.")
|| className.startsWith("com.android.internal.")
|| className.startsWith("com.google.android.maps.")
|| className.startsWith("com.google.android.gms.")
|| className.startsWith("dalvik.system.")
|| className.startsWith("org.apache.http.impl.client.DefaultRequestDirector");
}