2011-08-30 10 views

risposta

11

La risposta breve è no.

Robolectric è selettivo su quali classi intercetta e strumenti. Al momento in cui scriviamo, le uniche classi che saranno strumentati devono avere una corrispondenza nome di classe pienamente qualificato uno di questi selettori:

android.* 
com.google.android.maps.* 
org.apache.http.impl.client.DefaultRequestDirector 

La vera ragione per l'esistenza di Robolectric è che le classi fornite nel vaso tiro Android SDK eccezioni quando invocate in una JVM (cioè non su un emulatore o dispositivo). L'attività della tua applicazione ha un'origine che non è "ostile" (probabilmente non genera eccezioni quando vengono invocati metodi o costruttori). Lo scopo di Robolectric è quello di consentire di testare il codice dell'applicazione, cosa che altrimenti non sarebbe possibile a causa del modo in cui è stato scritto l'SDK. Alcuni degli altri motivi per cui è stato creato Robolectric sono stati:

  • L'SDK non ha sempre metodi che permettano di interrogare lo stato degli oggetti Android manipolati da codice dell'applicazione. Le ombre possono essere scritte per fornire l'accesso a questo stato.
  • Molte delle classi e metodi nel SDK di Android sono definitive e/o privata o protetta, rendendo difficile creare le dipendenze necessarie per il codice dell'applicazione che altrimenti sarebbero disponibili per il codice dell'applicazione.

Il codice potrebbe essere chiaramente modificato per ombreggiare qualsiasi classe. In passato si è parlato dell'estrazione delle feature di shadowing in una libreria autonoma, per aiutare a scrivere test usando altre API test-ostili.

Perché vuoi ombra la tua attività?

+0

Grazie per la spiegazione. Il motivo per cui voglio oscurare la mia attività è perché viene lanciato dalla mia applicazione con una chiamata a 'startActivityForResult (..)'. Ho questo codice: 'ShadowActivity shadowActivity = shadowOf (activityA); \t \t Intent startedIntent = shadowActivity.getNextStartedActivity(); \t \t ShadowIntent shadowIntent = shadowOf (startedIntent); \t \t assertThat (shadowIntent.getComponent(). GetClassName(), equalTo (activityB.class.getName())); 'Voglio ottenere le visualizzazioni dall'attivitàB. Usando l'API nativa, ho usato un 'ActivityMonitor' ma voglio sapere come farlo usando Robolectric. – kaneda

2

come aggiornamento, sono stato in grado di creare le ombre delle mie classi, fino a quando sono attento di impegnare la classe di ombra prima di ogni possibile pala agisce su quella classe. Così, seguendo le istruzioni, nella RoboRunner ho fatto:

@Override protected void bindShadowClasses() { 
    Robolectric.bindShadowClass(ShadowLog.class); 
    Robolectric.bindShadowClass(ShadowFlashPlayerFinder.class); 
} 

Ho già detto che sto barare un po '? La risposta originale sopra è (ovviamente) corretta. Quindi io uso questo per la mia vera classe:

package android.niftyco; 

public class FlashPlayerFinder { 
    .. . 

E la mia finta (ombra) è dietro nel mio pacchetto di prova, come ci si potrebbe aspettare:

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); 
    } 
} 
3

Sì, se si sottoclasse il RobolectricTestRunner, aggiungi un pacchetto personalizzato al costruttore e carica le tue classi Shadow nel metodo bindShadowClasses. Non c'è bisogno di usare il trucco del pacchetto Android. *.

(Nota: questo è con robolectric-1,1)

Ci sono una serie di ganci forniti nel RobolectricTestRunner # setupApplicationState che è possibile ignorare.

Ecco la mia realizzazione del 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); 
} 

}

Più metodi che è possibile creare una sottoclasse (da 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(); 
} 

Ecco dove si chiamano nel 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); 
} 
9

Questo ha significativamente modificato con Robolectric 2. È possibile specificare custom shadows in the configuration anziché scrivere il tuo TestRunner.

Ad esempio:

@Config(shadows = {ShadowAudioManager.class, ShadowContextWrapper.class}) 
2

potrebbe essere in ritardo, ma da qui: org.robolectric.bytecode.Setup, si potrebbe trovare ulteriori dettagli su ciò che le classi sono strumentati.

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"); 
    } 
Problemi correlati