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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With