Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.util.ConcurrentModificationException in JUnit tests

This one is kind of a shot in the dark. I'm getting the following error occasionally when running my robolectric unit test suite:

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)

I'm having a horrible time making light of this stacktrace and what could be going on. Has anybody run into this and found a solution?

I found one issue on Robolectric's github that suggests doing a clean before executing the tests. While it seems to minimize the number of times this test fails, it's still failing on occasion.

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

The test:

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

Update:

This issue goes away when not running Activity specific unit tests (similar to the following). For the time being I've had to comment out these tests.

I'm generally following the Robolectric approach found here - http://blog.blundell-apps.com/android-gradle-app-with-robolectric-junit-tests/ with some updates (since that article is a bit old).

I'm executing the tests with a ./gradlew test

like image 959
loeschg Avatar asked Feb 11 '15 23:02

loeschg


1 Answers

If you're using something like Fabric or Crashlytics, make sure to disable it in your Robolectric tests. We've been running into a very similar issue and through some local debugging, we were able to find the thread that was causing the issue. When Fabric initializes, it starts a background thread which accesses some resources. My thought is that this is primarily a bug with Robolectric in that it doesn't handle resource loading in a thread safe manner but there really is no need for Crashlytics in a unit testing environment so this is a quick fix.

Typically, a user will initialize Fabric or Crashlytics in their Application class. Robolectric allows you to make a "Test" Application class by prefixing your current class with the word "Test". In your original Application class, make a separate method for initializing the crash reporting system. In your TestApplication, override that method and make sure it is empty so Fabric is not initialized in your tests. For example:

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

This helped resolve the issue in our apps.

like image 147
plackemacher Avatar answered Oct 30 '22 15:10

plackemacher