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
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