2015-02-11 9 views
10

Questo è una specie di tiro al buio. Sto ottenendo il seguente errore di tanto in tanto quando si esegue il mio robolectric suite di test di unità:java.util.ConcurrentModificationException in JUnit tests

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) 

Sto avendo un momento orribile facendo luce di questo stacktrace e quello che potrebbe essere in corso. Qualcuno ha incontrato questo e trovato una soluzione?

Ho trovato un problema sul github di Robolectric che suggerisce di eseguire un clean prima di eseguire i test. Mentre sembra minimizzare il numero di volte che questo test fallisce, a volte fallisce.

MainActivityActivity.java:75 è setContentView(R.layout.activity_main);

La prova:

@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(); 
} 

Aggiornamento:

Questo problema va via quando non è in esecuzione test di unità specifica attività (simile al seguente) . Per il momento ho dovuto commentare questi test.

In genere seguo l'approccio Robolectric trovato qui - http://blog.blundell-apps.com/android-gradle-app-with-robolectric-junit-tests/ con alcuni aggiornamenti (poiché quell'articolo è un po 'vecchio).

sto eseguendo i test con un ./gradlew test

+1

dov'è il tuo codice? –

+0

aggiornato la mia domanda, @KickButtowski – loeschg

+0

Questo è molto intrigante :) Come stai facendo test? È in parallelo per caso? –

risposta

10

Se stai usando qualcosa come tessuto o Crashlytics, assicurarsi di disattivare nei vostri test Robolectric. Abbiamo riscontrato un problema molto simile a e tramite il debugging locale siamo riusciti a trovare il thread che causava il problema. Quando Fabric si inizializza, avvia un thread in background che accede ad alcune risorse. Il mio pensiero è che questo è principalmente un bug con Robolectric in quanto non gestisce il caricamento delle risorse in modo thread-safe, ma non c'è davvero bisogno di Crashlytics in un ambiente di testing unitario, quindi questa è una soluzione rapida.

In genere, un utente inizializza Fabric o Crashlytics nella loro classe Application. Robolectric ti permette di creare una classe "Test" Application prefiggendo la tua classe corrente con la parola "Test". Nella classe originale Application, creare un metodo separato per inizializzare il sistema di segnalazione degli arresti anomali. Nel tuo TestApplication, sostituisci quel metodo e assicurati che sia vuoto in modo che Fabric non sia inizializzato nei test. Per esempio:

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. 
    } 
} 

questo ha aiutato a risolvere il problema in nostri applicazioni.

+2

Felice di avere * il nostro * problema risolto :) – loeschg

+0

Grazie per aver condiviso la soluzione, ma la mia preoccupazione è in questo modo Crashlytics non è in grado di catturare gli arresti in modalità Debug che se sei in una grande organizzazione con molti tester che usa la versione di debug, altrimenti hai problemi (se usano la versione di rilascio per i test), allora questa è un'idea fantastica. – Hesam

+1

@Hesam se vuoi che funzioni nelle build di debug, quindi rimuovi la riga '.disabled (BuildConfig.DEBUG)' e poi sarà abilitata per tutte le build (tranne le build di test se hai la classe 'TestApp' presente) – plackemacher