2015-03-16 13 views
6

Ho iniziato a colpire uno strano ClassCastException in Mockito quando lo si utilizza con Robolectric. Quando eseguo gli stessi test non usando il runner Robolectric, tutto va bene, non viene lanciata alcuna eccezione.Mockito con Robolectric: "ClassCastException si è verificato durante la creazione del proxy"

Ecco traccia dello stack:

org.mockito.exceptions.base.MockitoException: 
ClassCastException occurred when creating the proxy. 
You might experience classloading issues, disabling the Objenesis cache *might* help (see MockitoConfiguration) 
    at com.compassrosetech.ccs.android.test.ObservableCacheDispatcherTest.setUp(ObservableCacheDispatcherTest.java:63) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) 
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24) 
    at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:236) 
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) 
    at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:158) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309) 
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160) 
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74) 
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211) 
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134) 
Caused by: java.lang.ClassCastException: kotlin.Function0$$EnhancerByMockitoWithCGLIB$$c0163e7f cannot be cast to org.mockito.cglib.proxy.Factory 
    at org.mockito.internal.creation.jmock.ClassImposterizer.createProxy(ClassImposterizer.java:128) 
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:63) 
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:56) 
    at org.mockito.internal.creation.CglibMockMaker.createMock(CglibMockMaker.java:23) 
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:26) 
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:51) 
    at org.mockito.Mockito.mock(Mockito.java:1243) 
    at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:30) 
    at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:16) 
    at org.mockito.internal.configuration.DefaultAnnotationEngine.createMockFor(DefaultAnnotationEngine.java:43) 
    at org.mockito.internal.configuration.DefaultAnnotationEngine.process(DefaultAnnotationEngine.java:66) 
    at org.mockito.internal.configuration.InjectingAnnotationEngine.processIndependentAnnotations(InjectingAnnotationEngine.java:71) 
    at org.mockito.internal.configuration.InjectingAnnotationEngine.process(InjectingAnnotationEngine.java:55) 
    at org.mockito.MockitoAnnotations.initMocks(MockitoAnnotations.java:108) 
    ... 29 more 

Il codice che sto utilizzando è il seguente:

@RunWith(ApplicationTestRunner.class) 
public class ObservableCacheDispatcherTest { 

    @Mock 
    private IDataMapper mockDataMapper; 

    @Mock 
    private ICache mockCache; 

    @Mock 
    private Function0<Object> mockLoader; 

    private ObservableCacheDispatcher cacheDispatcher; 


    @Before 
    public void setUp() { 
     MockitoAnnotations.initMocks(this); // <-- exception happens when executing this statement 
     cacheDispatcher = new ObservableCacheDispatcher(mockCache, mockDataMapper); 
    } 

    ... 
} 

Questo è il modo Robolectric corridore assomiglia:

public class ApplicationTestRunner extends RobolectricTestRunner { 

    //Maximun SDK Robolectric will compile (issues with SDK > 18) 
    private static final int MAX_SDK_SUPPORTED_BY_ROBOLECTRIC = 18; 

    private static final String ANDROID_MANIFEST_PATH = "../app/src/main/AndroidManifest.xml"; 
    private static final String ANDROID_MANIFEST_RES_PATH = "../app/src/main/res"; 

    /** 
    * Call this constructor to specify the location of resources and AndroidManifest.xml. 
    * 
    * @throws org.junit.runners.model.InitializationError 
    */ 
    public ApplicationTestRunner(Class<?> testClass) throws InitializationError { 
     super(testClass); 
    } 

    @Override protected AndroidManifest getAppManifest(Config config) { 
     return new AndroidManifest(Fs.fileFromPath(ANDROID_MANIFEST_PATH), 
       Fs.fileFromPath(ANDROID_MANIFEST_RES_PATH)) { 
      @Override 
      public int getTargetSdkVersion() { 
       return MAX_SDK_SUPPORTED_BY_ROBOLECTRIC; 
      } 
     }; 
    } 
} 

Che cosa dovrebbe Ho risolto per sbarazzarmi di questa eccezione?

+0

Interessante! Ho appena indovinato qualcosa in conflitto con Kotlin/Robolectric. Kotlin sostituisce ClassLoader? Spero che gli esperti di Kotlin si uniscano alla conversazione –

+0

Puoi controllare la soluzione da qui https://github.com/robolectric/robolectric-gradle-plugin/issues/75 –

+0

@EugenMartynov buon punto, ho aggiunto il tag "kotlin", forse Kotlin ragazzi aiuterebbero. –

risposta

1

Questo in realtà non è un problema di Kotlin ma Robolectric/Mockito. Credo che sia legato ai classloader. Se sostituisci la classe Kotlin con qualsiasi altra interfaccia di libreria (né JDK né la classe del tuo progetto) vedrai la stessa eccezione. Sfortunatamente non vedo soluzioni perché non capisco gli interni di Robolectric. È meglio chiedere a Robolectric o ai team Mockito su questo.

+0

Capito, Sergey, grazie per i chiarimenti. –

+0

Sto usando 'mockito' con' robolectric' senza alcun problema. Il codice simile funziona bene per me. È la versione specifica di 'robolectric' e' mockito'? –

+0

Sembra essere. Ho scoperto che il problema è causato da diversi programmi di caricamento di classe utilizzati dal runner di prova (Launcher/AsmInstrumentingClassLoader) e dal classloader di mockito (SearchingClassLoader), quindi la classe stub viene generata da una classe ma Proxy (che abbiamo provato a trasmettere) è stata caricata da un'altra. Richiede ulteriori indagini e sono abbastanza sicuro che i team di Mockito o Robolectric possano fare di meglio. –

1

Ho avuto un problema simile. Fondamentalmente, hai alcune classi che vuoi Mockito e altre classi che vuoi deridere con PowerMock. L'ho risolto aggiungendo l'annotazione @PowerMockIngore alla mia classe di test e specificando le classi che volevo prendere in giro usando regolarmente 'ol mockito. Quindi nel tuo caso, penso aggiungendo l'annotazione:

@PowerMockIgnore({"com.package.IDataMapper", "com.package.ICache" ..etc}) 

funzionerebbe.

1

Soluzione cautela!

Si prega di utilizzare a proprio rischio.

Prima di tutto bisogna ridefinire le interfacce di funzione Kotlin:

interface TestFunction0<R> : Function0<R> 
interface TestFunction1<T1, R> : Function1<T1, R> 

e modelli è:

val onUpdate: TestFunction0<Unit> = mock() 
7

Come suggerito

'disabilitare la cache Objenesis forza aiuto'

È possibile creare l'override di MockitoConfiguration.
Per farlo

  • Creare un pacchetto chiamato org.mockito.configuration sotto la tua src/test/java
  • Scrivi la classe MockitoConfiguration

classe File

package org.mockito.configuration; 

public class MockitoConfiguration extends DefaultMockitoConfiguration { 

    @Override 
    public boolean enableClassCache() { 
    return false; 
    } 
} 

Prova a creare nuovamente il tuo codice.

0

So che la domanda è un po 'vecchia, ma può aiutare qualcuno a cercare la risposta.

calcolata

@PowerMockIgnore({ "org.mockito.*"}) 

Dopo

@RunWith(ApplicationTestRunner.class) 

dovrebbe risolvere il problema.