Sto iniziando a utilizzare Robolectric in Android Studio. In un primo momento ho voluto creare un semplice test utilizzando hamcrest, che riportiamo di seguito:Robolectric 2.4 NoSuchMethodError non può essere trasmesso a RuntimeException durante la creazione di attività
@RunWith(CustomTestRunner.class)
@Config(emulateSdk = 18)
public class MainActivityTest {
private MainActivity mainActivity;
@Test
public void testMainActivity() {
mainActivity = buildActivity(MainActivity.class).create().get();
assertThat(mainActivity, notNullValue());
}
}
Quando si esegue il test genera la seguente eccezione:
java.lang.ClassCastException: java.lang.NoSuchMethodError cannot be cast to java.lang.RuntimeException
at org.robolectric.internal.ReflectionHelpers.callInstanceMethodReflectively(ReflectionHelpers.java:68)
at org.robolectric.util.ActivityController$1.run(ActivityController.java:115)
at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:268)
at org.robolectric.util.ActivityController.create(ActivityController.java:111)
at org.robolectric.util.ActivityController.create(ActivityController.java:122)
at com.enprodo.wakemethere.googleservices.geofence.GeofenceTest.testReceiver(GeofenceTest.java:31)
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:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:236)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:158)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
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)
La classe CustomTestRunner
si presenta così:
public class CustomTestRunner extends RobolectricTestRunner {
public CustomTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
@Override
protected AndroidManifest getAppManifest(Config config) {
String manifestProperty = System.getProperty("android.manifest");
if (config.manifest().equals(Config.DEFAULT) && manifestProperty != null) {
String resProperty = System.getProperty("android.resources");
String assetsProperty = System.getProperty("android.assets");
AndroidManifest androidManifest = new AndroidManifest(
Fs.fileFromPath(manifestProperty),
Fs.fileFromPath(resProperty),
Fs.fileFromPath(assetsProperty));
androidManifest.setPackageName("com.package");
return androidManifest;
}
return super.getAppManifest(config);
}
}
Posso aggiungere che MainActivity estende android.support.v7.app.ActionBarActivity
ma non ho idea se questo influisce sul comportamento.
Dopo qualche scavo nella fonte Robolectric appare l'errore si verifica quando questo viene eseguito:
ReflectionHelpers.callInstanceMethodReflectively(component, "performCreate", new ReflectionHelpers.ClassParameter(Bundle.class, bundle));
Il codice di callInstanceMethodReflectively
assomiglia a questo:
public static <R> R callInstanceMethodReflectively(final Object instance, final String methodName, ClassParameter... classParameters) {
try {
final Class[] classes = ClassParameter.getClasses(classParameters);
final Object[] values = ClassParameter.getValues(classParameters);
return traverseClassHierarchy(instance.getClass(), NoSuchMethodException.class, new InsideTraversal<R>() {
@Override
public R run(Class traversalClass) throws Exception {
Method declaredMethod = traversalClass.getDeclaredMethod(methodName, classes);
declaredMethod.setAccessible(true);
return (R) declaredMethod.invoke(instance, values);
}
});
} catch (InvocationTargetException e) {
throw (RuntimeException) e.getTargetException();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
io non sono un esperto di riflessione, ma sembra che in qualche modo non sia possibile trovare il metodo performCreate. Qualcuno ha riscontrato problemi simili? Qualcuno ha qualche idea di quale potrebbe essere il problema?