我正在尝试将
Espresso 2.0’s AndoridJUnitRunner与ActivityUnitTestCase集成.不过,我的测试在
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$1.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$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.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$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.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;
我还缺少什么让AndroidJUnitRunner使用ActivityUnitTestCase?任何帮助将不胜感激.
解决方法
我的头撞在墙上,窗户和各种家具后,终于让我的测试工作了!
从a response on its issue tracker起,我了解到,不能从仪器线程创建新的处理程序.这意味着必须在UI线程中调用startActivity().这导致3个解决方案.
使用仪器的runOnMainSync()
getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { activity = startActivity(new Intent(Intent.ACTION_MAIN),null); } });
2.创建自己的基类,扩展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 Object lastNonConfigurationInstance) { final AtomicReference<T> activityRef = new AtomicReference<>(); final Runnable activityRunnable = new Runnable() { @Override public void run() { activityRef.set(YourBaseActivityUnitTestCase.super.startActivity( intent,lastNonConfigurationInstance)); } }; if (Looper.myLooper() != Looper.getMainLooper()) { getInstrumentation().runOnMainSync(activityRunnable); } else { activityRunnable.run(); } return activityRef.get(); }
3.如果在测试方法中调用startActivity(),并且整个测试可以在主线程上运行,则可以使用@UiThreadTest简单地注释测试方法.
在我的测试中,我已经尝试了所有3种解决方案,但只有1& 2会奏效