tutorial tag qué manager instalar gtm google fragmento contenedor configurar como android dagger android-espresso

android - tag - gtm console



¿Puedo extender una aplicación personalizada en espresso? (4)

Estoy intentando configurar Dagger en mis pruebas de instrumentación Espresso para simular llamadas a recursos externos (servicios REST en este caso). El patrón que seguí en Robolectric para las pruebas de mi unidad fue ampliar mi clase de Aplicación de producción y anular los módulos Dagger con módulos de prueba que devolverían simulacros. Estoy intentando hacer lo mismo aquí, pero recibo una excepción ClassCastException en mis pruebas de Espresso cuando intento convertir la aplicación en mi aplicación personalizada.

Aquí está mi configuración hasta ahora:

Producción

Bajo app / src / main / java / com / mypackage / injection tengo:

MyCustomApplication

package com.mypackage.injection; import android.app.Application; import java.util.ArrayList; import java.util.List; import dagger.ObjectGraph; public class MyCustomApplication extends Application { protected ObjectGraph graph; @Override public void onCreate() { super.onCreate(); graph = ObjectGraph.create(getModules().toArray()); } protected List<Object> getModules() { List<Object> modules = new ArrayList<Object>(); modules.add(new AndroidModule(this)); modules.add(new RemoteResourcesModule(this)); modules.add(new MyCustomModule()); return modules; } public void inject(Object object) { graph.inject(object); } }

Que utilizo de la siguiente manera:

BaseActividad

package com.mypackage.injection.views; import android.app.Activity; import android.os.Bundle; import com.mypackage.injection.MyCustomApplication; public abstract class MyCustomBaseActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((MyCustomApplication)getApplication()).inject(this); } }

Actividad a prueba

package com.mypackage.views.mydomain; // imports snipped for bevity public class MyActivity extends MyBaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //snip } }

Configuración de espresso

Bajo app / src / androidTest / java / com / mypackage / injection tengo:

MyCustomEspressoApplication

package com.mypackage.injection; import java.util.ArrayList; import java.util.List; import dagger.ObjectGraph; public class MyCustomEspressoApplication extends MyCustomApplication { private AndroidModule androidModule; private MyCustomModule myCustomModule; private EspressoRemoteResourcesModule espressoRemoteResourcesModule; @Override public void onCreate() { super.onCreate(); graph = ObjectGraph.create(getModules().toArray()); } protected List<Object> getModules() { List<Object> modules = new ArrayList<Object>(); modules.add(getAndroidModule()); modules.add(getEspressoRemoteResourcesModule()); modules.add(getMyCustomModule()); return modules; } public void inject(Object object) { graph.inject(object); } public AndroidModule getAndroidModule() { if (this.androidModule == null) { this.androidModule = new AndroidModule(this); } return this.androidModule; } public MyCustomModule getMyCustomModule() { if (this.myCustomModule == null) { this.myCustomModule = new MyCustomModule(); } return this.myCustomModule; } public EspressoRemoteResourcesModule getEspressoRemoteResourcesModule() { if (this.espressoRemoteResourcesModule == null) { this.espressoRemoteResourcesModule = new EspressoRemoteResourcesModule(); } return this.espressoRemoteResourcesModule; } }

Mi prueba de espresso, bajo app / src / android Test / com / mypackage / espresso:

package com.mypackage.espresso; // imports snipped for brevity @RunWith(AndroidJUnit4.class) @LargeTest public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity>{ private MyActivity myActivity; public MyActivityTest() { super(MyActivity.class); } @Before public void setUp() throws Exception { super.setUp(); injectInstrumentation(InstrumentationRegistry.getInstrumentation()); myActivity = getActivity(); } @After public void tearDown() throws Exception { super.tearDown(); } @Test public void testWhenTheActionBarButtonIsPressedThenThePlacesAreListed() { //The next line is where the runtime exception occurs. MyCustomEspressoApplication app = (MyCustomEspressoApplication)getInstrumentation().getTargetContext().getApplicationContext(); //I''ve also tried getActivity().getApplication() and // getActivity.getApplicationContext() with the same results //snip } }

Mi AndroidManifest.xml

(He visto muchas respuestas con respecto a la ClassCastException en las clases de aplicaciones personalizadas anteriormente, y la mayoría de ellas apuntan a una propiedad "android: name" que falta en el nodo de la aplicación. Estoy pegando esto aquí para mostrar que este no es el caso Por lo que yo puedo decir.)

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.mypackage"> <!-- snip --> <application android:name=".injection.MyCustomApplication" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <!-- snip --> </application> <!-- snip --> </manifest>

construir.gradle

buildscript { repositories { mavenCentral() jcenter() } } apply plugin: ''com.android.application'' apply plugin: ''idea'' android { testOptions { unitTests.returnDefaultValues = true } lintOptions { abortOnError false } packagingOptions { exclude ''LICENSE.txt'' exclude ''META-INF/LICENSE'' exclude ''META-INF/LICENSE.txt'' exclude ''META-INF/NOTICE'' } compileSdkVersion 21 buildToolsVersion "21.1.2" defaultConfig { applicationId "com.mypackage" minSdkVersion 15 targetSdkVersion 21 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } } idea { module { testOutputDir = file(''build/test-classes/debug'') } } dependencies { compile project('':swipeablecardview'') compile fileTree(dir: ''libs'', include: [''*.jar'']) compile ''com.android.support:support-annotations:21.0.3'' compile ''com.android.support:appcompat-v7:21.0.3'' compile ''com.squareup:javawriter:2.5.0'' compile (''com.squareup.dagger:dagger:1.2.2'') { exclude module: ''javawriter'' } compile (''com.squareup.dagger:dagger-compiler:1.2.2'') { exclude module: ''javawriter'' } compile ''com.melnykov:floatingactionbutton:1.1.0'' compile ''com.android.support:cardview-v7:21.0.+'' compile ''com.android.support:recyclerview-v7:21.0.+'' // compile ''se.walkercrou:google-places-api-java:2.1.0'' compile ''org.apache.httpcomponents:httpclient-android:4.3.5.1'' compile ''commons-io:commons-io:1.3.2'' testCompile ''org.hamcrest:hamcrest-integration:1.3'' testCompile ''org.hamcrest:hamcrest-core:1.3'' testCompile ''org.hamcrest:hamcrest-library:1.3'' testCompile(''junit:junit:4.12'') testCompile ''org.mockito:mockito-core:1.+'' testCompile(''org.robolectric:robolectric:3.0-SNAPSHOT'') testCompile(''org.robolectric:shadows-support-v4:3.0-SNAPSHOT'') androidTestCompile ''org.mockito:mockito-core:1.+'' androidTestCompile(''com.android.support.test.espresso:espresso-core:2.0'') { exclude group: ''javax.inject'' exclude module: ''javawriter'' } androidTestCompile(''com.android.support.test:testing-support-lib:0.1'') }

El stacktrace:

java.lang.ClassCastException: com.mypackage.injection.MyCustomApplication no se puede convertir en el nombre de la aplicación. invokeNative (Método nativo) en java.lang.reflect.Method.invoke (Method.java:511) en org.junit.runners.model.FrameworkMethod $ 1.runReflectiveCall (FrameworkMethod.java:45) en org.junit.internal.runners .model.ReflectiveCallable.run (ReflectiveCallable.java:15) en org.junit.runners.model.FrameworkMethod.invokeExplosively (FrameworkMethod.java:42) en org.junit.internal.runners.statements.InvokeMethod.viv.viv.viv. : 20) en org.junit.internal.runners.statements.RunBefores.evaluate (RunBefores.java:28) en org.junit.internal.runners.statements.RunAfters.evaluate (RunAfters.java:30) en org.junit. runners.ParentRunner.runLeaf (ParentRunner.java:263) en org.junit.runners.BlockJUnit4ClassRunner.runChild ( BlockJUnit4ClassRunner.java:68) en org.junit.runners.BlockJUnit4ClassRunner.runChild (BlockJUnit4ClassRunner.java:47) en el grupo de personas. .schedule (ParentRunner.java:60) en org.junit.runners.ParentRunner.runChildren (ParentRunner.java:229) en org.junit.runners.ParentRunner.access $ 000 (ParentRunner.java:50) en org.junit.runners .ParentRunner $ 2.evaluate (ParentRunner.java:222) en org.junit.runners.ParentRunner.run (ParentRunner.java:300) en org.junit.runners.Suite.runChild (Suite.java:128) en org.junit .runners.Suite.runChild (Suite.java:24) en org.junit.runners.ParentRunner $ 3.run (ParentRunner.java:231) en org.junit.runners.ParentRunner $ 1.schedule (ParentRunner.java:60) en org.junit.runners.ParentRunner.runChildren (ParentRunner.java:229) en org.junit.runners.ParentRunner.access $ 000 (ParentRunner.java:50) en org.junit.runners.ParentRunner $ 2.evaluate.Personal 222) en org.junit.runners.ParentRunner.run (ParentRunner. java: 300) en org.junit.runner.JUnitCore.run (JUnitCore.java:157) en org.junit.runner.JUnitCore.run (JUnitCore.java:136) en android.support.test.runner.AndroidJUnitRunner.onStart (AndroidJUnitRunner.java:270) en android.app.Instrumentation $ InstrumentationThread.run (Instrumentation.java:1551)

He leído los documentos de Espresso y Dagger y he buscado en Github sin éxito alguno. Apreciaría cualquier ayuda que alguien pueda proporcionar. Gracias por adelantado.

Editar # 1

Seguí la sugerencia de Daniel de extender el corredor de prueba y verificar el VerifyError, y obtuve el siguiente seguimiento de pila:

java.lang.ExceptionInInitializerError at org.mockito.internal.creation.cglib.ClassImposterizer.createProxyClass(ClassImposterizer.java:95) at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:57) at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49) at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24) at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33) at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59) at org.mockito.Mockito.mock(Mockito.java:1285) at org.mockito.Mockito.mock(Mockito.java:1163) at com.mypackage.injection.EspressoRemoteResourcesModule.<init>(EspressoRemoteResourcesModule.java:17) at com.mypackage.injection.MyCustomEspressoApplication.getEspressoRemoteResourcesModule(MyCustomEspressoApplication.java:52) at com.mypackage.injection.MyCustomEspressoApplication.getModules(MyCustomEspressoApplication.java:24) at com.mypackage.injection.MyCustomApplication.onCreate(MyCustomApplication.java:18) at com.mypackage.injection.MyCustomEspressoApplication.onCreate(MyCustomEspressoApplication.java:16) at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:999) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4151) at android.app.ActivityThread.access$1300(ActivityThread.java:130) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1255) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4745) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167) at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217) at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145) at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117) at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109) at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105) at org.mockito.cglib.proxy.Enhancer.<clinit>(Enhancer.java:70)             at org.mockito.internal.creation.cglib.ClassImposterizer.createProxyClass(ClassImposterizer.java:95)             at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:57)             at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49)             at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24)             at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)             at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)             at org.mockito.Mockito.mock(Mockito.java:1285)             at org.mockito.Mockito.mock(Mockito.java:1163)             at com.mypackage.injection.EspressoRemoteResourcesModule.<init>(EspressoRemoteResourcesModule.java:17)             at com.mypackage.injection.MyCustomEspressoApplication.getEspressoRemoteResourcesModule(MyCustomEspressoApplication.java:52)             at com.mypackage.injection.MyCustomEspressoApplication.getModules(MyCustomEspressoApplication.java:24)             at com.mypackage.injection.MyCustomApplication.onCreate(MyCustomApplication.java:18)             at com.mypackage.injection.MyCustomEspressoApplication.onCreate(MyCustomEspressoApplication.java:16)             at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:999)             at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4151)             at android.app.ActivityThread.access$1300(ActivityThread.java:130)             at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1255)             at android.os.Handler.dispatchMessage(Handler.java:99)             at android.os.Looper.loop(Looper.java:137)             at android.app.ActivityThread.main(ActivityThread.java:4745)             at java.lang.reflect.Method.invokeNative(Native Method)             at java.lang.reflect.Method.invoke(Method.java:511)             at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)             at dalvik.system.NativeStart.main(Native Method) 04-29 06:40:28.594 1016-1016/? W/ActivityManager﹕ Error in app com.mypackage running instrumentation ComponentInfo{com.mypackage.test/com.mypackage.EspressoTestRunner}: 04-29 06:40:28.594 1016-1016/? W/ActivityManager﹕ java.lang.VerifyError 04-29 06:40:28.594 1016-1016/? W/ActivityManager﹕ java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils

Esto me señaló a Mockito. Me faltaban las bibliotecas necesarias de mockito y dexmaker.

Actualicé mis dependencias a:

androidTestCompile ''org.mockito:mockito-core:1.10.19'' androidTestCompile ''com.google.dexmaker:dexmaker:1.2'' androidTestCompile (''com.google.dexmaker:dexmaker-mockito:1.2'') { exclude module: ''hamcrest-core'' exclude module: ''mockito-core'' } androidTestCompile(''com.android.support.test.espresso:espresso-core:2.0'') { exclude group: ''javax.inject'' }

También anulé MyCustomModule, que necesitaba incluir EspressoRemoteResourcesModule. Una vez que hice esto las cosas empezaron a funcionar.


Con un corredor de instrumentación personalizado, puede anular newApplication y hacer que newApplication una instancia de la aplicación por defecto que no sea la aplicación predeterminada.

public class MyRunner extends AndroidJUnitRunner { @Override public Application newApplication(ClassLoader cl, String className, Context context) throws Exception { return super.newApplication(cl, MyCustomEspressoApplication.class.getName(), context); } }

Asegúrese de actualizar testInstrumentationRunner con el nombre de su corredor personalizado.


Me tomó un día entero para obtener la respuesta completa.

Paso 1: anular AndroidJUnitRunner

public class TestRunner extends AndroidJUnitRunner { @Override public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return super.newApplication(cl, TestApplication.class.getName(), context); } }

Paso 2: reemplaza el AndroidJunitRunner existente en build.gradle

defaultConfig { ... // testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner ''com.xi_zz.androidtest.TestRunner'' }

Paso 3: Agregue com.android.support.test: runner to build.gradle

androidTestCompile ''com.android.support.test:runner:0.5'' androidTestCompile ''com.android.support.test.espresso:espresso-core:2.2.2''

Paso 4: Solo si tienes este error

Warning:Conflict with dependency ''com.android.support:support-annotations''. Resolved versions for app (25.2.0) and test app (23.1.1) differ. See http://g.co/androidstudio/app-test-app-conflict for details.

Luego, agregue una línea más:

androidTestCompile ''com.android.support:support-annotations:25.2.0'' androidTestCompile ''com.android.support.test:runner:0.5'' androidTestCompile ''com.android.support.test.espresso:espresso-core:2.2.2''

Finalmente, prueba si funciona.

@RunWith(AndroidJUnit4.class) public class MockApplicationTest { @Rule public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class); @Test public void testApplicationName() throws Exception { assertEquals("TestApplication", mActivityRule.getActivity().getApplication().getClass().getSimpleName()); } }


No he probado esto ampliamente en todos los casos, pero puede probar una Regla personalizada para especificar su clase de Aplicación personalizada por caso de prueba en lugar de todos los casos de prueba aplicados por el corredor personalizado. He tenido éxito con los siguientes en casos simples:

public class ApplicationTestRule<T extends Application> extends UiThreadTestRule { Class<T> appClazz; boolean wait = false; T app; public ApplicationTestRule(Class<T> applicationClazz) { this(applicationClazz, false); } public ApplicationTestRule(Class<T> applicationClazz, boolean wait) { this.appClazz = applicationClazz; this.wait = wait; } @Override public Statement apply(final Statement base, Description description) { return new ApplicationStatement(super.apply(base, description)); } private void terminateApp() { if (app != null) { app.onTerminate(); } } public void createApplication() throws IllegalAccessException, ClassNotFoundException, InstantiationException { app = (T) InstrumentationRegistry.getInstrumentation().newApplication(this.getClass().getClassLoader(), appClazz.getName(), InstrumentationRegistry.getInstrumentation().getTargetContext()); InstrumentationRegistry.getInstrumentation().callApplicationOnCreate(app); } private class ApplicationStatement extends Statement { private final Statement mBase; public ApplicationStatement(Statement base) { mBase = base; } @Override public void evaluate() throws Throwable { try { if (!wait) { createApplication(); } mBase.evaluate(); } finally { terminateApp(); app = null; } } } }

Luego, en tu caso de prueba, crea la regla:

@Rule public ApplicationTestRule<TestApplication> appRule = new ApplicationTestRule<>(TestApplication.class,true);

Tenga en cuenta que el segundo parámetro es opcional. Si es falso o está apagado, la aplicación personalizada se crea cada vez antes de cada caso de prueba. Si se establece en verdadero, debe llamar a appRule.createApplication() antes de la lógica de la aplicación.


Si está probando un módulo de biblioteca, puede crear una clase de aplicación personalizada y registrarla en el manifiesto del paquete de prueba:

root / library-module / src / androidTest / AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:name="path.to.TestApplication" /> </manifest>

No hay reglas, no hay corredores.