Java.util.ConcurrentModificationException en las pruebas de JUnit
Esta es una especie de disparo en la oscuridad. Estoy recibiendo el error siguiente de vez en cuando al ejecutar mi unidad de prueba de unidad robolectric:
java.util.ConcurrentModificationException at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:966) at java.util.LinkedList$ListItr.next(LinkedList.java:888) at org.robolectric.shadows.ShadowResources.getOverlayedThemeValue(ShadowResources.java:294) at org.robolectric.shadows.ShadowResources.findAttributeValue(ShadowResources.java:284) at org.robolectric.shadows.ShadowResources.attrsToTypedArray(ShadowResources.java:187) at org.robolectric.shadows.ShadowResources.access$000(ShadowResources.java:51) at org.robolectric.shadows.ShadowResources$ShadowTheme.obtainStyledAttributes(ShadowResources.java:489) at android.content.res.Resources$Theme.obtainStyledAttributes(Resources.java) at android.content.Context.obtainStyledAttributes(Context.java:416) at android.view.View.__constructor__(View.java:3317) at org.robolectric.util.ReflectionHelpers$3.run(ReflectionHelpers.java:144) at org.robolectric.util.ReflectionHelpers.traverseClassHierarchy(ReflectionHelpers.java:241) at org.robolectric.util.ReflectionHelpers.callInstanceMethod(ReflectionHelpers.java:138) at org.robolectric.internal.Shadow.invokeConstructor(Shadow.java:73) at org.robolectric.shadows.ShadowView.__constructor__(ShadowView.java:109) at android.view.View.<init>(View.java) at android.widget.TextView.<init>(TextView.java) at com.getbase.floatingactionbutton.FloatingActionsMenu.createLabels(FloatingActionsMenu.java:461) at com.getbase.floatingactionbutton.FloatingActionsMenu.onFinishInflate(FloatingActionsMenu.java:447) at android.view.LayoutInflater.rInflate(LayoutInflater.java:763) at android.view.LayoutInflater.rInflate(LayoutInflater.java:758) at android.view.LayoutInflater.rInflate(LayoutInflater.java:758) at android.view.LayoutInflater.inflate(LayoutInflater.java:492) at android.view.LayoutInflater.inflate(LayoutInflater.java:397) at android.view.LayoutInflater.inflate(LayoutInflater.java:353) at android.support.v7.app.ActionBarActivityDelegateBase.setContentView(ActionBarActivityDelegateBase.java:228) at android.support.v7.app.ActionBarActivity.setContentView(ActionBarActivity.java:102) at com.myapp.app.view.activity.MainActivityActivity.onCreate(MainActivityActivity.java:75) at android.app.Activity.performCreate(Activity.java:5133) at org.robolectric.util.ReflectionHelpers$3.run(ReflectionHelpers.java:144) at org.robolectric.util.ReflectionHelpers.traverseClassHierarchy(ReflectionHelpers.java:241) at org.robolectric.util.ReflectionHelpers.callInstanceMethod(ReflectionHelpers.java:138) at org.robolectric.util.ActivityController$1.run(ActivityController.java:114) at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:309) at org.robolectric.shadows.CoreShadowsAdapter$2.runPaused(CoreShadowsAdapter.java:47) at org.robolectric.util.ActivityController.create(ActivityController.java:110) at com.myapp.app.BaseActivityRobolectricTest.startActivity(BaseActivityRobolectricTest.java:58) at com.myapp.app.BaseActivityRobolectricTest.startActivity(BaseActivityRobolectricTest.java:34) at com.myapp.app.view.activity.MainActivityActivityTest.setupActivity(MainActivityActivityTest.java:52) 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:234) 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:167) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:48) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) at com.sun.proxy.$Proxy2.processTestClass(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:360) at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)
Estoy teniendo un horrible tiempo haciendo luz de este stacktrace y lo que podría estar pasando. ¿Alguien se ha topado con esto y ha encontrado una solución?
- Android Studio + Robolectric + Gradle Class Not Found Excepción
- Roboeléctrico con test.R.java
- Robolectric 3.0 java.lang.NoSuchMethodException en crear actividad
- Android Robolectric unidad de prueba de Marshmallow PermissionHelper
- Prueba de ActiveAndroid con Robolectric
Encontré un problema en el github de Robolectric que sugiere hacer un clean
antes de ejecutar las pruebas. Aunque parece reducir al mínimo el número de veces que esta prueba falla, todavía falla ocasionalmente.
MainActivityActivity.java:75 es setContentView(R.layout.activity_main);
La prueba:
@Before public void setupActivity() { setupAuthObjectGraphWithFakeData(); startActivity(RequestReviewsActivity.class, new ParentModule(), null); } protected void startActivity(Class<T> activityClass, Object module, Intent intent) { mActivityController = buildActivity(activityClass); if(intent != null) { mActivityController.withIntent(intent); } mActivity = mActivityController.get(); List<Object> modules = new ArrayList<>(); modules.addAll(mActivity.getModules()); modules.add(module); ObjectGraph objectGraph; if(mActivity instanceof AuthedActivity) { objectGraph = BaseApplication.getInstance().getAuthObjectGraph().plus(modules.toArray()); } else { objectGraph = BaseApplication.getInstance().getAppObjectGraph().plus(modules.toArray()); } mActivity.setObjectGraph(objectGraph); mActivityController.create(mBundle).start().resume(); } @Test public void testViewsAreAvailable() { assertThat(getActivity().mToolbar).isNotNull(); assertThat(getActivity().mContent).isNotNull(); assertThat(getActivity().mContentShadow).isNotNull(); assertThat(getActivity().mRequestRootFab).isNotNull(); assertThat(getActivity().mRequestReviewsList).isNotNull(); assertThat(getActivity().mRequestReviewsMainContent).isNotNull(); }
Actualizar:
Este problema desaparece cuando no se realizan pruebas unitarias específicas de actividad (similares a las siguientes). Por el momento he tenido que comentar estas pruebas.
Estoy siguiendo generalmente el acercamiento de Robolectric encontrado aquí – http://blog.blundell-apps.com/android-gradle-app-with-robolectric-junit-tests/ con algunas actualizaciones (puesto que ese artículo es un pedacito viejo).
Estoy ejecutando las pruebas con una ./gradlew test
- Servicios Roboeléctricos y de Intención
- Robolectric inflar vistas personalizadas para pruebas
- Influencia de diseño personalizado con Fragmentos en Robolectric no funciona
- ¿Cómo puedo analizar una suite de pruebas junit?
- Prueba de Android Handler.postDelayed
- NPE al obtener Robolectric ShadowApplication con Volley y Dagger
- Intellij + gradle + robolectric + espresso
- Pruebe la carpeta de recursos con Robolectric y Gradle
Si está usando algo como Fabric o Crashlytics, asegúrese de desactivarlo en sus pruebas Robolectric. Hemos estado funcionando en una edición muy similar y con una cierta depuración local, pudimos encontrar el hilo que causaba el problema. Cuando Fabric se inicializa, inicia un subproceso de fondo que accede a algunos recursos. Mi pensamiento es que esto es principalmente un error con Robolectric en que no maneja la carga de recursos en una manera segura de hilo, pero realmente no hay necesidad de Crashlytics en un entorno de prueba de unidad por lo que esta es una solución rápida.
Normalmente, un usuario inicializará Fabric o Crashlytics en su clase Application
. Robolectric le permite hacer una clase de "Prueba" de la Application
prefijando su clase actual con la palabra "Prueba". En su clase de Application
original, haga un método separado para inicializar el sistema de informes de fallos. En su TestApplication
, TestApplication
ese método y asegúrese de que está vacío por lo que Fabric no se inicializa en las pruebas. Por ejemplo:
App.java
public class App extends Application { @Override public void onCreate() { super.onCreate(); setupCrashReporting(); } protected void setupCrashReporting() { CrashlyticsCore core = new CrashlyticsCore.Builder() .disabled(BuildConfig.DEBUG) .build(); Crashlytics crashlytics = new Crashlytics.Builder() .core(core) .build(); Fabric.with(this, crashlytics); } }
TestApp.java
public class TestApp extends App { @Override protected void setupCrashReporting() { // Do nothing. } }
Esto ayudó a resolver el problema en nuestras aplicaciones.