androidgradleandroid-gradle-pluginrobolectricrobolectric-gradle-plugin

ClassCastException: NoClassDefFoundError cannot be cast to RuntimeException


I am working on upgrading my codebase to Gradle 2.2 and Android Studio 1.0. I am currently trying to get Robolectric 2.4 working, but I am experiencing a strange issue when I try and run Unit Tests. The issue occurs only after a gradle clean; running the test suite multiple times will produce passing tests (as expected). When I run the tests after a clean, I get the following error:

java.lang.ClassCastException: java.lang.NoClassDefFoundError cannot be cast to java.lang.RuntimeException

I have traced the error back to this call:

Activity activity = Robolectric.setupActivity(MainActivity.class);

I experience this error whether I use the Robolectric gradle plugin (org.robolectric:robolectric-gradle-plugin:0.14.0) or the JC and K Android unit test plugin (com.github.jcandksolutions.gradle:android-unit-test:2.1.1).

I found this issue referenced on the Robolectric Github, but it doesn't look like it has been addressed yet: https://github.com/robolectric/robolectric/issues/1385

This issue is also referenced in the android studio unit test plugin, under 'Trouble Shooting': https://github.com/evant/android-studio-unit-test-plugin

My current sample code is here: https://github.com/KioKrofovitch/robolectric-upgrade-test

I have been able to run the api-android-16 project on the Robolectric samples without seeing this issue, though api-android-19 and api-android-21 projects fail for other reasons. I cannot see what they are doing differently such that they do not get this failure. https://github.com/robolectric/robolectric-samples

Has anyone found a workaround to this? Running the tests twice is not a good workaround for CI tools like Jenkins or Travis.

EDIT: Embedding code samples

My top level build.gradle where I add the JC and K Unit test library:

buildscript {
    repositories {
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.0.0'
        classpath 'com.github.jcandksolutions.gradle:android-unit-test:2.1.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

My project level build.gradle, where I add in robolectric:

apply plugin: 'com.android.application'

repositories {
    mavenCentral()
}

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    defaultConfig {
        applicationId "com.example.robolectrictest"
        minSdkVersion 15
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }

        androidTest {
            setRoot('src/androidTest')
        }
    }
}

// Must be after Android plugin
apply plugin: 'android-unit-test'

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.3'

    //androidTestCompile 'junit:junit:4.10'
    //androidTestCompile 'org.robolectric:robolectric:2.4'

    // Testing frameworks
    testCompile 'junit:junit:4.10'
    testCompile 'org.robolectric:robolectric:2.4'
}

My very basic Activity, everything has been left as the template creates it:

public class MainActivity extends ActionBarActivity {

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


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

My dummy test, where I can't get to my assert due to the setupActivity() method error:

@Config(emulateSdk = 18)
@RunWith(RobolectricTestRunner.class)

public class ApplicationTest {

    @org.junit.Test
    public void testDummy() throws Exception {

        Activity activity = Robolectric.setupActivity(MainActivity.class);

        assertTrue(true);
    }

}

EDIT #2: To run the tests I invoke the following commands from the top level directory of my project:

./gradlew clean
./gradlew test

I have also tried running the tests outside the gradle wrapper, and get the same results

gradle clean
gradle test

Solution

  • This was confirmed as an issue with Robolectric 2.4 (Issue #1385). The issue has been closed by Erich Douglass today, with the following comment:

    We are working on appcompat support for 3.0. Until then, there's not much you can do.

    https://github.com/robolectric/robolectric/issues/1385

    So until 3.0 is released, I will be using the following workaround:

    ./gradlew clean
    ./gradlew assemble
    ./gradlew test
    

    Once the assemble has run, it has created all the items that Robolectric apparently needs. Then you can run your tests successfully. This solution is better than just running ./gradlew build or ./gradlew test twice, because it can be implemented both locally as well as on a CI tool like Travis or Jenkins. The syntax for running multiple Gradle tasks in Jenkins is leaving a single space between each task:

    assemble test