我正在编写单元测试以涵盖测试API。我使用robolectric和gradle,还必须添加multidex来支持大型apk构建。出乎意料的是我无法运行测试而无法理解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();
}
}
你知道根本原因是什么吗?
这是我的gradle构建
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'
更新
控制台输出:
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"
}
}
答案 0 :(得分:2)
请确保将Robolectric与test
(非 androidTest
)文件夹一起使用,并且依赖项应同样设置为testCompile
:
testCompile 'org.robolectric:robolectric:3.3.2' //don't use androidTestCompile here!!!
如果您遇到Robolectric问题,请尝试以下步骤:
.m2
文件夹以使maven的依赖关系再次下载gradlew --refresh-dependencies
并清理和重建项目尝试调整Robolectric测试参数:
@RunWith(RobolectricGradleTestRunner.class)
@Config(application = TestApplication.class,
constants = BuildConfig.class,
sdk = 21)
您可以尝试使用RobolectricTestRunner.class
代替RobolectricGradleTestRunner.class
,而忽略@Config
注释。如果这不起作用,您可以创建一个类:
public class EmptyApplication extends Application {
}
并使用:
@Config(application = EmptyApplication.class)
更新:请注意Robolectric测试(在test
文件夹中)和Instrumented Android测试(在androidTest
文件夹中)之间的区别。 Robolectric测试是在IDE(在您的计算机的JVM上)运行的测试,模拟真实设备上可用的Android类(例如,Context
)。仪表化Android测试在Android手机或模拟器上运行,并使用Android SDK中的真实 Context
等。以下是来自空项目的检测测试的示例:
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());
}
}