1

I have a build on TeamCity that runs unit tests via Gradle. Intermitentlly, tests that involve fragments or activities fail with a ClassNotFoundException for classes like android.support.v4.app.FragmentTransitionCompat21$ViewRetriever or android.support.v4.app.ActivityCompat21$SharedElementCallback21. The tests fail when starting the fragment, and I've tried all of the methods to start a fragment from this question - How can I test fragments with Robolectric?.

Here's an example for a test:

@Test
public void ContactSupportFragment_CallBtnClicked_CallWasMade() throws Exception
{
    ContactSupportFragment fragment = new ContactSupportFragment();
    startFragment(fragment);

    LinearLayout btnCall = (LinearLayout) fragment.getView().findViewById(R.id.contact_support_call_btn);
    btnCall.performClick();

    Mockito.verify(techSupportCall, Mockito.times(1)).call(Mockito.any(Context.class),
      Mockito.eq(Robolectric.application.getString(R.string.tech_support_phone_number)));
}

Here's an example for a stack trace:

java.lang.NoClassDefFoundError: android/support/v4/app/FragmentTransitionCompat21$ViewRetriever
at android.support.v4.app.FragmentManagerImpl.beginTransaction(FragmentManager.java:481)
at org.robolectric.util.FragmentTestUtil.startFragment(FragmentTestUtil.java:25)
at com.asurion.solutohome.callsupport.ContactSupportFragmentTest.ContactSupportFragment_CallBtnClicked_CallWasMade(ContactSupportFragmentTest.java:70)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:236)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:158)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
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:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassNotFoundException: android.support.v4.app.FragmentTransitionCompat21$ViewRetriever
at org.robolectric.bytecode.AsmInstrumentingClassLoader.loadClass(AsmInstrumentingClassLoader.java:88)
at android.support.v4.app.FragmentManagerImpl.$$robo$$FragmentManagerImpl_917e_beginTransaction(FragmentManager.java:481)
at android.support.v4.app.FragmentManagerImpl.beginTransaction(FragmentManager.java)
at org.robolectric.util.FragmentTestUtil.startFragment(FragmentTestUtil.java:25)
at com.asurion.solutohome.callsupport.ContactSupportFragmentTest.ContactSupportFragment_CallBtnClicked_CallWasMade(ContactSupportFragmentTest.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:236)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:158)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
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 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
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 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)

It hasn't happened locally yet, only on the TeamCity agent, however I haven't found anything different between my machine and the agent (same SDK, same Gradle build etc.). What can cause these exceptions?

Community
  • 1
  • 1
Oded
  • 2,239
  • 2
  • 18
  • 16
  • If you try to run it second time without clean. Does it pass? – Eugen Martynov Dec 31 '14 at 18:13
  • @EugenMartynov it passes, but since it fails intermitenty I couldn't be sure it's because I'm not running clean... If it is related, what does it mean the problem is? – Oded Jan 01 '15 at 07:28
  • There are several issues opened on the Robolectic Github (https://github.com/robolectric/robolectric/issues/1321). The issue popups on CI builds and passes when run second time without clean. It looks like issue of robolectric gradle plugin, which mess with tasks name and proper classpathes – Eugen Martynov Jan 01 '15 at 17:25
  • @EugenMartynov I added an additional assemble build step to the Teamcity build and so far all the builds pass. Thanks! – Oded Jan 04 '15 at 11:28

3 Answers3

2

So following @EugenMartynov's suggestion I added an additional assemble build step to my Teamcity build before building running the tests and so far all the builds pass. It seems that Robolectric indeed has some issues with CI builds.

So to summarize, instead of building once and running tests, my build runs:

  • clean and build
  • build again and run tests

All of the tests pass every build.

Oded
  • 2,239
  • 2
  • 18
  • 16
  • I was able to relaunch the assemble test through gradle as a preTest task before running the tests and this didn't resolve the issue for me. – markshiz Jan 06 '15 at 23:51
  • I had to manually run `gradle assemble; gradle test` in order for this to work successfully. it appears as if something is not fully resolving in the build system. – markshiz Jan 06 '15 at 23:59
0

I am not much into the topic of Robolectric, but the stacktrace you sent looks like you forgot to add the binaries or the jar, which includes the class android.support.v4.app.FragmentTransitionCompat21 (i think this class is included in the android SDK), to your classpath.

  • I'm not very experienced with Gradle yet, but as far as I can tell the concept of classpaths is a bit different with it. The module already has a dependency on the support library, and from what I understand adding it to the classpath only affects the build process itself. – Oded Dec 31 '14 at 14:34
0

As per @Fastas answer. You can run

gradle test

This will resolve the issue, but as for it's exact cause, it is as of yet unknown.

For my own part, I think this is related to dependencies from my app project not fully resolving the first build of an exterior app-test project. I am using the following code in app-test/build.gradle.

dependencies {
    // other dependencies ...

    compile project(':app')
    testCompile androidModule.android.applicationVariants.toList().first().javaCompile.classpath
    testCompile androidModule.android.applicationVariants.toList().first().javaCompile.outputs.files
    testCompile files(androidModule.plugins.findPlugin("com.android.application").getBootClasspath())
}

Above, possibly classpath or outputs.files is not fully resolved until the assemble task finishes.

Update: March 23, 2015

Moving my test suite to the experimental test support within the app module resolved this issue for me.

markshiz
  • 2,501
  • 22
  • 28