Если я напишу пользовательский Shadow для моей активности и зарегистрирую его с помощью RobolectricTestRunner, будет ли инфраструктура перехватывать Activity с моей пользовательской Shadow всякий раз, когда она начинается?
Спасибо.
Если я напишу пользовательский Shadow для моей активности и зарегистрирую его с помощью RobolectricTestRunner, будет ли инфраструктура перехватывать Activity с моей пользовательской Shadow всякий раз, когда она начинается?
Спасибо.
Короткий ответ - нет.
Robolectric избирательно относится к тому, какие классы он перехватывает и инструменты. На момент написания этой статьи единственными классами, которые будут задействованы, должно быть полное имя класса, соответствующее одному из этих селекторов:
android.*
com.google.android.maps.*
org.apache.http.impl.client.DefaultRequestDirector
Вся причина существования Robolectric заключается в том, что классы, предоставляемые в базах Android SDK, генерируют исключения при вызове в JVM (т.е. не на эмуляторе или устройстве). В вашем приложении Activity есть источник, который не является "враждебным" (он, вероятно, не генерирует исключений при вызове методов или конструкторов). Целью Robolectric является дать возможность проверить ваш код приложения, что в противном случае было бы невозможным из-за того, как написан SDK. Некоторые из других причин создания Robolectric:
Код может быть явно изменен для теневого класса. В прошлом говорилось о том, чтобы извлечь функции затенения в автономную библиотеку, чтобы помочь писать тесты с использованием другого тестового враждебного api.
Почему вы хотите скрыть свою активность?
Это существенно изменилось с помощью Robolectric 2. Вы можете указать пользовательские тени в конфигурации вместо написания собственного TestRunner.
Например:
@Config(shadows = {ShadowAudioManager.class, ShadowContextWrapper.class})
Да, если вы подклассифицируете RobolectricTestRunner, добавьте настраиваемый пакет в конструктор и загрузите свои классы Shadow в метод bindShadowClasses. Не нужно использовать андроид. * Трюк пакета.
(Примечание: это с robolectric-1.1)
В RobolectricTestRunner # setupApplicationState есть несколько крючков, которые вы можете переопределить.
Здесь моя реализация RobolectricTestRunner.
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);
}
}
Дополнительные методы для подкласса (из RobolectricTestRunner.class)
/**
* Override this method to bind your own shadow classes
*/
protected void bindShadowClasses() {
}
/**
* Override this method to reset the state of static members before each test.
*/
protected void resetStaticState() {
}
/**
* Override this method if you want to provide your own implementation of Application.
* <p/>
* This method attempts to instantiate an application instance as specified by the AndroidManifest.xml.
*
* @return An instance of the Application class specified by the ApplicationManifest.xml or an instance of
* Application if not specified.
*/
protected Application createApplication() {
return new ApplicationResolver(robolectricConfig).resolveApplication();
}
Здесь, где они вызываются в Robolectric TestRunner:
public void setupApplicationState(final RobolectricConfig robolectricConfig) {
setupLogging();
ResourceLoader resourceLoader = createResourceLoader(robolectricConfig);
Robolectric.bindDefaultShadowClasses();
bindShadowClasses();
resourceLoader.setLayoutQualifierSearchPath();
Robolectric.resetStaticState();
resetStaticState();
DatabaseConfig.setDatabaseMap(this.databaseMap);//Set static DatabaseMap in DBConfig
Robolectric.application = ShadowApplication.bind(createApplication(), resourceLoader);
}
Как обновление, мне удалось создать тени моих собственных классов, пока я стараюсь привязать теневой класс до того, как какой-либо возможный загрузчик будет действовать на этот класс. Итак, согласно инструкциям, в RoboRunner я сделал:
@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 pretend that Flash is installed\n");
return(true);
}
}
Возможно, вам будет поздно, но отсюда: org.robolectric.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");
}