Androiderson
Androiderson

Reputation: 17083

NoClassDefFoundError implementing IdlingResource

I need to tell Espresso to wait until my Activity is idle. I'm using the interface IdlingResource to achieve this. Here's the code I'm using for testing:

    public class MyActivity extends Activity implements IdlingResource {

        private boolean isIdle;
        ResourceCallback resourceCallback;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_my);

            final Button myButton = (Button) findViewById(R.id.myButton);
            final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    myButton.setVisibility(View.VISIBLE);
                    progressBar.setVisibility(View.GONE);
                    isIdle = true;
                }
            }, 3000);
        }

        public void ButtonClicked(View view) {
            Toast.makeText(MyActivity.this, "OK", Toast.LENGTH_SHORT).show();
        }

        @Override
        public String getName() {
            return getClass().getName();
        }

        @Override
        public boolean isIdleNow() {

            if(isIdle){
                resourceCallback.onTransitionToIdle();
                return true;
            }

            return false;
        }

        @Override
        public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
            this.resourceCallback = resourceCallback;
        }
    }

Espresso automatically waits for an AsyncTask to finish, so if I use AnsycTask I don't even need to implement this interface. The problem is that I'm using a library to communicate to the network and it seems to not use AnsyncTasks or Espresso doesn't know that.

My problem is that the code above works fine only on my device (Motorola XT1058 or Moto X) if I run the very same test on the emulator (Genymotion) or any other device, I end up with the following exception:

junit.framework.AssertionFailedError: Exception in constructor: testA (java.lang.NoClassDefFoundError: rapnaveia.com.br.myapplication.MyActivity
at rapnaveia.com.br.myapplication.ApplicationTest.<init>(ApplicationTest.java:16)
at java.lang.reflect.Constructor.constructNative(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at junit.runner.BaseTestRunner.getTest(BaseTestRunner.java:118)
at android.test.AndroidTestRunner.getTest(AndroidTestRunner.java:149)
at android.test.AndroidTestRunner.setTestClassName(AndroidTestRunner.java:57)
at android.test.suitebuilder.TestSuiteBuilder.addTestClassByName(TestSuiteBuilder.java:80)
at android.test.InstrumentationTestRunner.parseTestClass(InstrumentationTestRunner.java:443)
at android.test.InstrumentationTestRunner.parseTestClasses(InstrumentationTestRunner.java:424)
at android.test.InstrumentationTestRunner.onCreate(InstrumentationTestRunner.java:370)
at com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner.onCreate(GoogleInstrumentationTestRunner.java:114)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4435)
at android.app.ActivityThread.access$1300(ActivityThread.java:141)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1316)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5103)
at java.lang.reflect.Method.invokeNative(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner.onStart(GoogleInstrumentationTestRunner.java:167)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)

An here the code of my test class:

public class ApplicationTest extends ActivityInstrumentationTestCase2<MyActivity> {

    public ApplicationTest() {
        super(MyActivity.class);
    }

    public void testA(){
        Espresso.registerIdlingResources(getActivity());
        Espresso.onView(withId(R.id.myButton)).perform(click());
    }
}

build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "rapnaveia.com.br.myapplication"
        minSdkVersion 15
        targetSdkVersion 20
        versionCode 1
        versionName "1.0"

        testApplicationId 'br.com.rapnaveia'
        testInstrumentationRunner 'com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner'
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile files('libs/espresso-1.1-bundled.jar')
}

Upvotes: 4

Views: 834

Answers (1)

The problem is in the gradle configuration. Your project is trying to compile the espresso jar twice.

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile files('libs/espresso-1.1-bundled.jar')
}

With this configuration, the espresso jar is compiled both by your main project and your test project. So, when you try to execute the test, the main project fails to build properly, causing the error (And I think weird that you could run the test in your device).

To run your tests, all you have to do is remove the compile instruction from the dependency of your test target.

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

The android test project will use the espresso jar compiled in the main project, since that the main project is "compiled" into the test project.

Upvotes: 4

Related Questions