Reputation: 6989
I am writing unit tests to cover API with tests. I use robolectric and gradle, also have to add multidex to support large apk build. Unexpectedly I can not run the test and can not understand the stacktrace
java.lang.NullPointerException: parentLoader == null && !nullAllowed
at java.lang.ClassLoader.<init>(ClassLoader.java:210)
at java.lang.ClassLoader.<init>(ClassLoader.java:202)
at java.security.SecureClassLoader.<init>(SecureClassLoader.java:48)
at java.net.URLClassLoader.<init>(URLClassLoader.java:710)
at java.net.URLClassLoader.<init>(URLClassLoader.java:555)
at org.codehaus.classworlds.RealmClassLoader.<init>(RealmClassLoader.java:94)
at org.codehaus.classworlds.RealmClassLoader.<init>(RealmClassLoader.java:83)
at org.codehaus.classworlds.DefaultClassRealm.<init>(DefaultClassRealm.java:116)
at org.codehaus.classworlds.ClassWorld.newRealm(ClassWorld.java:100)
at org.apache.maven.artifact.ant.AbstractArtifactTask.getContainer(AbstractArtifactTask.java:490)
at org.apache.maven.artifact.ant.AbstractArtifactTask.lookup(AbstractArtifactTask.java:457)
at org.apache.maven.artifact.ant.AbstractArtifactTask.initSettings(AbstractArtifactTask.java:290)
at org.apache.maven.artifact.ant.AbstractArtifactTask.execute(AbstractArtifactTask.java:750)
at org.robolectric.internal.dependency.MavenDependencyResolver.getLocalArtifactUrls(MavenDependencyResolver.java:40)
at org.robolectric.internal.dependency.CachedDependencyResolver.getLocalArtifactUrls(CachedDependencyResolver.java:43)
at org.robolectric.internal.InstrumentingClassLoaderFactory.getSdkEnvironment(InstrumentingClassLoaderFactory.java:39)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:187)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54)
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:152)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
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.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:59)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:262)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1837)
@RunWith(RobolectricGradleTestRunner.class)
@Config(application = TestApplication.class,
constants = BuildConfig.class,
sdk = 21)
public class ApiTest {
private MockWebServer webServer;
private IApi api;
private TestUtils testUtils;
@Before
public void setUp() throws Exception {
Log.d(App.TAG, "Test setUp");
testUtils = new TestUtils();
webServer = new MockWebServer();
webServer.start();
webServer.setDispatcher(new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
MockResponse response;
if (request.getPath().equals("savingsgoals")) {
response = new MockResponse().setResponseCode(200)
.setBody(testUtils.readString("json/goals.json"));
} else if (request.getPath().equals("savingsgoals/" + TestApp.Const.GOAL_ID + "/feed")) {
response = new MockResponse().setResponseCode(200)
.setBody(testUtils.readString("json/feed.json"));
} else if (request.getPath().equals("savingsrules")) {
response = new MockResponse().setResponseCode(200)
.setBody(testUtils.readString("json/rules.json"));
} else if (request.getPath().equals("user/" + TestApp.Const.USER_ID)) {
response = new MockResponse().setResponseCode(200)
.setBody(testUtils.readString("json/user.json"));
} else {
response = new MockResponse().setResponseCode(404);
}
return response;
}
});
HttpUrl url = webServer.url("/");
api = ApiModule.getApiInterface(url.toString());
}
@Test
public void testGoals() throws Exception {
TestSubscriber<SavingsGoals> testSubscriber = new TestSubscriber();
api.getSavingsGoals().subscribe(testSubscriber);
testSubscriber.assertNoErrors();
testSubscriber.assertValueCount(1);
SavingsGoals goals = testSubscriber.getOnNextEvents().get(0);
goals.getSavingsGoals();
}
}
Do you have an idea what is root cause?
Here is my gradle build
androidTestCompile 'org.robolectric:robolectric:3.3.2'
androidTestCompile('com.squareup.okhttp:mockwebserver:2.7.0', {
exclude group: 'com.squareup.okio', module: 'okio'
})
androidTestCompile 'junit:junit:4.12'
UPDATE
Console output:
Error:Execution failed for task ':app:transformClassesWithDexForDebugAndroidTest'. > com.android.build.api.transform.TransformException: java.lang.RuntimeException: java.lang.RuntimeException: Unable to pre-dex 'D:\Users\John\.gradle\caches\modules-2\files-2.1\com.thoughtworks.xstream\xstream\1.4.8\520d90f30f36a0d6ba2dc929d980831631ad6a92\xstream-1.4.8.jar' to 'C:\workspace\TestRobolectricTest\app\build\intermediates\pre-dexed\androidTest\debug\xstream-1.4.8_54d21a03bcf95b493d9c102453945dde45691be3.jar'
allprojects {
tasks.withType(JavaCompile) {
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
}
}
Upvotes: 0
Views: 1269
Reputation: 21487
Please make sure you use Robolectric with the test
(not androidTest
) folders and the dependency should be likewise set with testCompile
:
testCompile 'org.robolectric:robolectric:3.3.2' //don't use androidTestCompile here!!!
If you have problems with Robolectric, some steps to try include:
.m2
folder to cause the dependencies from maven to be downloaded againgradlew --refresh-dependencies
and cleaning and rebuilding the projectTry adjusting your Robolectric test parameters:
@RunWith(RobolectricGradleTestRunner.class)
@Config(application = TestApplication.class,
constants = BuildConfig.class,
sdk = 21)
You can try using RobolectricTestRunner.class
instead of RobolectricGradleTestRunner.class
and leaving out the @Config
annotation. If that doesn't work, you can create a class:
public class EmptyApplication extends Application {
}
And use that:
@Config(application = EmptyApplication.class)
Update: note the difference between a Robolectric test (in the test
folder) and an Instrumented Android test (in the androidTest
folder). Robolectric tests are tests run in the IDE (on your computer's JVM) that simulate the Android classes (e.g., Context
) available on a real device. Instrumented Android tests run on an Android handset or an emulator and use the real Context
etc. from the Android SDK. The following is the example of an Instrumented test from an empty project:
package example.github.com.robolectricbug;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("example.github.com.robolectricbug", appContext.getPackageName());
}
}
Upvotes: 3