ActivityUnitTestCase arroja RuntimeException cuando se ejecuta con AndroidJUnitRunner

Estoy intentando integrar AndoridJUnitRunner de Espresso 2.0 con ActivityUnitTestCase. Sin embargo, mis pruebas se estropean cuando startActivity () intenta inicializar mMockParent = new MockParent () .

Esto es lo que hice:

Cree un nuevo proyecto con Intellij 14 CE y realice algunos cambios en 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') } 

Escriba una prueba de unidad de estilo 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()); } } 

Ejecute la prueba, obteniendo una traza de pila.

 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) 

Este rastro de pila es en realidad un

 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 

Que se produce cuando startActivity () intenta inicializar 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; 

¿Estoy perdiendo algo más para que AndroidJUnitRunner funcione con ActivityUnitTestCase? Cualquier ayuda sería muy apreciada.

Después de golpear mi cabeza contra la pared, ventanas y varios muebles, finalmente conseguí mis pruebas de trabajo!

A partir de una respuesta en su seguimiento de problemas , aprendí que no se pueden crear nuevos Handlers desde el hilo de instrumentación. Lo que significa startActivity () debe ser invocado en el subproceso de la interfaz de usuario. Lo que lleva a 3 soluciones.

1.Use Instrumentation's runOnMainSync()

 getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { activity = startActivity(new Intent(Intent.ACTION_MAIN), null, null); } }); 

2.Create su propia clase base que extiende ActivityUnitTestCase y reemplazar 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. Si llama a startActivity () en un método de prueba y toda su prueba puede ejecutarse en el hilo principal, simplemente puede anotar su método de prueba con @UiThreadTest.

En mis pruebas, he probado todas las 3 soluciones, pero solo 1 & 2 funcionará .

  • `RenamingDelegatingContext` está obsoleto. ¿Cómo probamos SQLite db ahora?
  • ¿Cómo forzar un cambio de configuración en una prueba Android Robolectric?
  • Mock Android Application class para pruebas con Robolectric en App con Dagger 2
  • Android: Unidad de pruebas de aplicaciones Android con Robolectric y Mockito
  • ¿Cómo configurar gradle para usar logback-classic SOLAMENTE para las pruebas de unidad en Android?
  • Pruebas de unidades de Android fallan para tipo de compilación de depuración
  • ¿Hay alguien por ahí usando Robolectric sin Maven en IntelliJ?
  • ¿Por qué obtengo un NoClassDefFoundError al ejecutar mi prueba en ActionBarActivity?
  • ¿Existe una manera de escribir una prueba de unidad para una API de destino
  • Pruebas de escritura para aplicaciones Flow and Mortar
  • Sugar ORM necesita registros para ser guardado cada vez mientras que la prueba de unidad?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.