ActivityUnitTestCase 在使用 AndroidJUnitRunner 运行 时抛出 RuntimeException
ActivityUnitTestCase throws RuntimeException when ran with AndroidJUnitRunner
我正在尝试整合 Espresso 2.0's AndoridJUnitRunner with ActivityUnitTestCase. However, my tests are crashing when startActivity() tries to initialize mMockParent = new MockParent()。
这是我所做的:
使用 Intellij 14 CE 创建一个新项目并对 build.gradle 进行一些更改。
android{
defaultConfig {
applicationId "com.noob.testing"
minSdkVersion 9
targetSdkVersion 21
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.+'
androidTestCompile('org.mockito:mockito-core:1.9.5')
androidTestCompile('com.google.dexmaker:dexmaker:1.2')
androidTestCompile('com.google.dexmaker:dexmaker-mockito:1.2')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.0')
androidTestCompile('com.android.support.test:testing-support-lib:0.1')
}
编写 JUnit4 风格的单元测试。
@RunWith(AndroidJUnit4.class)
public class MainActivityJUnit4Test extends ActivityUnitTestCase<MainActivity> {
public MainActivityJUnit4Test() {
super(MainActivity.class);
}
MainActivity activity;
@Before
public void setup() throws Exception {
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
super.setUp();
ContextThemeWrapper context = new ContextThemeWrapper(getInstrumentation().getTargetContext(), R.style.AppTheme);
setActivityContext(context);
activity = startActivity(new Intent(Intent.ACTION_MAIN), null, null);
}
@Test
public void baseCase() {
TextView tv = (TextView) activity.findViewById(R.id.tv);
Assert.assertEquals("Hello World", tv.getText());
}
}
运行 测试,获取堆栈跟踪。
junit.framework.AssertionFailedError
at junit.framework.Assert.fail(Assert.java:48)
at junit.framework.Assert.assertTrue(Assert.java:20)
at junit.framework.Assert.assertNotNull(Assert.java:218)
at junit.framework.Assert.assertNotNull(Assert.java:211)
at android.test.ActivityUnitTestCase.startActivity(ActivityUnitTestCase.java:147)
at com.sdchang.testing.MainActivityJUnit4Test.setup(MainActivityJUnit4Test.java:32)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access[=13=]0(ParentRunner.java:50)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:24)
at org.junit.runners.ParentRunner.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access[=13=]0(ParentRunner.java:50)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:270)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
这个堆栈跟踪实际上是
的重新抛出
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
发生在 startActivity() tries to initialize mMockParent = new MockParent() 时:
ComponentName cn = new ComponentName(mActivityClass.getPackage().getName(),
mActivityClass.getName());
intent.setComponent(cn);
ActivityInfo info = new ActivityInfo();
CharSequence title = mActivityClass.getName();
mMockParent = new MockParent();
String id = null;
我是否遗漏了任何让 AndroidJUnit运行ner 与 ActivityUnitTestCase 一起工作的东西?任何帮助将不胜感激。
在用头撞墙、windows 和各种家具之后,我的测试终于成功了!
从a response on its issue tracker,我了解到无法从检测线程创建新的处理程序。这意味着必须在 UI 线程中调用 startActivity()。这导致 3 个解决方案。
1.Use 仪表的 runOnMainSync()
getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
activity = startActivity(new Intent(Intent.ACTION_MAIN), null, null);
}
});
2.Create 你自己的基础 class 扩展 ActivityUnitTestCase 并覆盖 startActivity
@Override
protected T startActivity(final Intent intent, final Bundle savedInstanceState,
final Object lastNonConfigurationInstance) {
return startActivityOnMainThread(intent, savedInstanceState, lastNonConfigurationInstance);
}
private T startActivityOnMainThread(final Intent intent, final Bundle savedInstanceState,
final Object lastNonConfigurationInstance) {
final AtomicReference<T> activityRef = new AtomicReference<>();
final Runnable activityRunnable = new Runnable() {
@Override
public void run() {
activityRef.set(YourBaseActivityUnitTestCase.super.startActivity(
intent, savedInstanceState, lastNonConfigurationInstance));
}
};
if (Looper.myLooper() != Looper.getMainLooper()) {
getInstrumentation().runOnMainSync(activityRunnable);
} else {
activityRunnable.run();
}
return activityRef.get();
}
3.If 你在测试方法中调用 startActivity() 并且你的整个测试可以 运行 在主线程上,你可以简单地用 @UiThreadTest 注释你的测试方法。
在我的测试中,我尝试了所有 3 种解决方案,但只有 1 和 2 可行。
我正在尝试整合 Espresso 2.0's AndoridJUnitRunner with ActivityUnitTestCase. However, my tests are crashing when startActivity() tries to initialize mMockParent = new MockParent()。
这是我所做的:
使用 Intellij 14 CE 创建一个新项目并对 build.gradle 进行一些更改。
android{
defaultConfig {
applicationId "com.noob.testing"
minSdkVersion 9
targetSdkVersion 21
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.+'
androidTestCompile('org.mockito:mockito-core:1.9.5')
androidTestCompile('com.google.dexmaker:dexmaker:1.2')
androidTestCompile('com.google.dexmaker:dexmaker-mockito:1.2')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.0')
androidTestCompile('com.android.support.test:testing-support-lib:0.1')
}
编写 JUnit4 风格的单元测试。
@RunWith(AndroidJUnit4.class)
public class MainActivityJUnit4Test extends ActivityUnitTestCase<MainActivity> {
public MainActivityJUnit4Test() {
super(MainActivity.class);
}
MainActivity activity;
@Before
public void setup() throws Exception {
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
super.setUp();
ContextThemeWrapper context = new ContextThemeWrapper(getInstrumentation().getTargetContext(), R.style.AppTheme);
setActivityContext(context);
activity = startActivity(new Intent(Intent.ACTION_MAIN), null, null);
}
@Test
public void baseCase() {
TextView tv = (TextView) activity.findViewById(R.id.tv);
Assert.assertEquals("Hello World", tv.getText());
}
}
运行 测试,获取堆栈跟踪。
junit.framework.AssertionFailedError
at junit.framework.Assert.fail(Assert.java:48)
at junit.framework.Assert.assertTrue(Assert.java:20)
at junit.framework.Assert.assertNotNull(Assert.java:218)
at junit.framework.Assert.assertNotNull(Assert.java:211)
at android.test.ActivityUnitTestCase.startActivity(ActivityUnitTestCase.java:147)
at com.sdchang.testing.MainActivityJUnit4Test.setup(MainActivityJUnit4Test.java:32)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access[=13=]0(ParentRunner.java:50)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:24)
at org.junit.runners.ParentRunner.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access[=13=]0(ParentRunner.java:50)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:270)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
这个堆栈跟踪实际上是
的重新抛出java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
发生在 startActivity() tries to initialize mMockParent = new MockParent() 时:
ComponentName cn = new ComponentName(mActivityClass.getPackage().getName(),
mActivityClass.getName());
intent.setComponent(cn);
ActivityInfo info = new ActivityInfo();
CharSequence title = mActivityClass.getName();
mMockParent = new MockParent();
String id = null;
我是否遗漏了任何让 AndroidJUnit运行ner 与 ActivityUnitTestCase 一起工作的东西?任何帮助将不胜感激。
在用头撞墙、windows 和各种家具之后,我的测试终于成功了!
从a response on its issue tracker,我了解到无法从检测线程创建新的处理程序。这意味着必须在 UI 线程中调用 startActivity()。这导致 3 个解决方案。
1.Use 仪表的 runOnMainSync()
getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
activity = startActivity(new Intent(Intent.ACTION_MAIN), null, null);
}
});
2.Create 你自己的基础 class 扩展 ActivityUnitTestCase 并覆盖 startActivity
@Override
protected T startActivity(final Intent intent, final Bundle savedInstanceState,
final Object lastNonConfigurationInstance) {
return startActivityOnMainThread(intent, savedInstanceState, lastNonConfigurationInstance);
}
private T startActivityOnMainThread(final Intent intent, final Bundle savedInstanceState,
final Object lastNonConfigurationInstance) {
final AtomicReference<T> activityRef = new AtomicReference<>();
final Runnable activityRunnable = new Runnable() {
@Override
public void run() {
activityRef.set(YourBaseActivityUnitTestCase.super.startActivity(
intent, savedInstanceState, lastNonConfigurationInstance));
}
};
if (Looper.myLooper() != Looper.getMainLooper()) {
getInstrumentation().runOnMainSync(activityRunnable);
} else {
activityRunnable.run();
}
return activityRef.get();
}
3.If 你在测试方法中调用 startActivity() 并且你的整个测试可以 运行 在主线程上,你可以简单地用 @UiThreadTest 注释你的测试方法。
在我的测试中,我尝试了所有 3 种解决方案,但只有 1 和 2 可行。