java.util.ConcurrentModificationException 在 JUnit 测试中
java.util.ConcurrentModificationException in JUnit tests
这有点像是在黑暗中拍摄的。 运行 在我的 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[=11=]0(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.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.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.run(ActivityController.java:114)
at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:309)
at org.robolectric.shadows.CoreShadowsAdapter.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.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.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.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:53)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:229)
at org.robolectric.RobolectricTestRunner.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.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)
我很难理解这个堆栈跟踪以及可能发生的事情。有没有人 运行 解决这个问题并找到了解决方案?
我在 Robolectric 的 github 上发现了一个问题,建议在执行测试之前先做一个 clean
。虽然它似乎最大限度地减少了此测试失败的次数,但它仍然偶尔会失败。
MainActivityActivity.java:75 是 setContentView(R.layout.activity_main);
测试:
@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();
}
更新:
如果没有 运行ning Activity 特定单元测试(类似于以下内容),此问题就会消失。目前我不得不注释掉这些测试。
我通常遵循此处找到的 Robolectric 方法 - http://blog.blundell-apps.com/android-gradle-app-with-robolectric-junit-tests/ 进行了一些更新(因为那篇文章有点旧)。
我正在使用 ./gradlew test
执行测试
如果您使用的是 Fabric 或 Crashlytics 之类的东西,请确保在 Robolectric 测试中将其禁用。 我们遇到了一个非常相似的问题,通过一些本地调试,我们能够找到导致该问题的线程。当 Fabric 初始化时,它会启动一个访问某些资源的后台线程。我的想法是,这主要是 Robolectric 的一个错误,因为它不以线程安全的方式处理资源加载,但在单元测试环境中确实不需要 Crashlytics,因此这是一个快速修复。
通常,用户会在 Application
class 中初始化 Fabric 或 Crashlytics。 Robolectric 允许您通过在当前 class 前加上单词 "Test" 来创建 "Test" Application
class。在您原来的 Application
class 中,制作一个单独的方法来初始化崩溃报告系统。在您的 TestApplication
中,重写该方法并确保它是空的,这样 Fabric 就不会在您的测试中被初始化。例如:
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);
}
}
测试App.java
public class TestApp extends App {
@Override
protected void setupCrashReporting() {
// Do nothing.
}
}
这有助于解决我们的 应用中的问题。
这有点像是在黑暗中拍摄的。 运行 在我的 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[=11=]0(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.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.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.run(ActivityController.java:114)
at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:309)
at org.robolectric.shadows.CoreShadowsAdapter.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.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.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.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:53)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:229)
at org.robolectric.RobolectricTestRunner.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.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)
我很难理解这个堆栈跟踪以及可能发生的事情。有没有人 运行 解决这个问题并找到了解决方案?
我在 Robolectric 的 github 上发现了一个问题,建议在执行测试之前先做一个 clean
。虽然它似乎最大限度地减少了此测试失败的次数,但它仍然偶尔会失败。
MainActivityActivity.java:75 是 setContentView(R.layout.activity_main);
测试:
@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();
}
更新:
如果没有 运行ning Activity 特定单元测试(类似于以下内容),此问题就会消失。目前我不得不注释掉这些测试。
我通常遵循此处找到的 Robolectric 方法 - http://blog.blundell-apps.com/android-gradle-app-with-robolectric-junit-tests/ 进行了一些更新(因为那篇文章有点旧)。
我正在使用 ./gradlew test
如果您使用的是 Fabric 或 Crashlytics 之类的东西,请确保在 Robolectric 测试中将其禁用。 我们遇到了一个非常相似的问题,通过一些本地调试,我们能够找到导致该问题的线程。当 Fabric 初始化时,它会启动一个访问某些资源的后台线程。我的想法是,这主要是 Robolectric 的一个错误,因为它不以线程安全的方式处理资源加载,但在单元测试环境中确实不需要 Crashlytics,因此这是一个快速修复。
通常,用户会在 Application
class 中初始化 Fabric 或 Crashlytics。 Robolectric 允许您通过在当前 class 前加上单词 "Test" 来创建 "Test" Application
class。在您原来的 Application
class 中,制作一个单独的方法来初始化崩溃报告系统。在您的 TestApplication
中,重写该方法并确保它是空的,这样 Fabric 就不会在您的测试中被初始化。例如:
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);
}
}
测试App.java
public class TestApp extends App {
@Override
protected void setupCrashReporting() {
// Do nothing.
}
}
这有助于解决我们的 应用中的问题。