2015-02-11 87 views
10

這是一種黑暗中的鏡頭。JUnit測試中的java.util.ConcurrentModificationException

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) 

我有一個可怕的時間使這個堆棧跟蹤的光,有什麼可以怎麼回事:運行我robolectric單元測試時,我偶爾會收到以下錯誤。有沒有人遇到這個問題並找到解決方案?

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

更新:

此問題消失沒有運行活動的具體單元測試(類似於以下)時, 。目前我不得不評論這些測試。

我通常遵循Robolectric的方法 - http://blog.blundell-apps.com/android-gradle-app-with-robolectric-junit-tests/有一些更新(因爲這篇文章有點舊)。

我執行的測試與./gradlew test

+1

你的代碼在哪裏? – 2015-02-11 23:18:33

+0

更新了我的問題,@KickButtowski – loeschg 2015-02-11 23:23:10

+0

這是非常有趣的:)你如何運行測試?這是平行的嗎? – 2015-02-16 17:03:58

回答

10

如果你使用像織物或Crashlytics,請確保您的Robolectric測試來禁用它。 我們遇到了一個非常類似的問題,通過一些本地調試,我們能夠找到導致問題的線程。當Fabric初始化時,它會啓動後臺線程來訪問一些資源。我的想法是,這主要是Robolectric的一個bug,它不能以線程安全的方式處理資源加載,但在單元測試環境中確實不需要Crashlytics,所以這是一個快速修復。

通常,用戶將在Application類中初始化Fabric或Crashlytics。 Robolectric允許您通過在當前班級中添加「測試」字樣來製作「測試」Application類。在您的原始Application類中,創建一個用於初始化故障報告系統的單獨方法。在您的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); 
    } 
} 

TestApp.java

public class TestApp extends App { 
    @Override 
    protected void setupCrashReporting() { 
     // Do nothing. 
    } 
} 

這有助於解決這個問題我們應用。

+2

很高興有*我們的問題解決了:) – loeschg 2015-07-02 20:22:09

+0

感謝您分享解決方案,但我的擔心是這樣Crashlytics無法捕捉調試模式下的崩潰,如果您在一個擁有大量測試人員的大型組織中使用調試版本,那麼你在問題,否則(如果他們使用發佈版本進行測試),那麼這是一個很棒的想法。 – Hesam 2015-11-10 23:11:33

+1

@Hesam如果你想讓它在調試版本中工作,那麼只需刪除'.disabled(BuildConfig.DEBUG)'行,然後它將爲所有的版本啓用(除了測試版本,只要你有'TestApp'類目前) – plackemacher 2015-11-11 03:03:26