medium last implement google compiler annotationprocessor java android unit-testing dagger-2

java - last - Pruebas de unidad Android con Dagger 2



implement dagger 2 android (5)

De hecho, tuve el mismo problema y encontré una solución muy simple. Esta no es la mejor solución posible, pero resolverá su problema.

Crea una clase similar en tu módulo de aplicación:

public class ActivityTest<T extends ViewModelBase> { @Inject public T vm; }

Luego, en tu AppComponent agrega:

void inject(ActivityTest<LoginFragmentVM> activityTest);

Entonces podrás inyectar eso en tu clase de prueba.

public class HelloWorldEspressoTest extends ActivityTest<LoginFragmentVM> { @Rule public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class); @Test public void listGoesOverTheFold() throws InterruptedException { App.getComponent().inject(this); vm.email.set("1234"); closeSoftKeyboard(); } }

Tengo una aplicación de Android que usa Dagger 2 para inyección de dependencia. También estoy usando las últimas herramientas de compilación de gradle que permiten una variante de compilación para las pruebas unitarias y una para las pruebas de instrumentación. Estoy usando java.util.Random en mi aplicación, y quiero simular esto para probarlo. Las clases que estoy probando no usan ninguna cosa de Android, por lo que solo son clases regulares de Java.

En mi código principal, defino un Component en una clase que amplía la clase de Application , pero en las pruebas de la unidad no estoy usando una Application . Traté de definir un Module prueba y un Component , pero Dagger no generará el Component . También intenté usar el Component que definí en mi aplicación e intercambiar el Module cuando lo construí, pero el Component la aplicación no tiene métodos de inject para mis clases de prueba. ¿Cómo puedo proporcionar una implementación simulada de Random para probar?

Aquí hay un código de muestra:

Solicitud:

public class PipeGameApplication extends Application { private PipeGame pipeGame; @Singleton @Component(modules = PipeGameModule.class) public interface PipeGame { void inject(BoardFragment boardFragment); void inject(ConveyorFragment conveyorFragment); } @Override public void onCreate() { super.onCreate(); pipeGame = DaggerPipeGameApplication_PipeGame.create(); } public PipeGame component() { return pipeGame; } }

Módulo:

@Module public class PipeGameModule { @Provides @Singleton Random provideRandom() { return new Random(); } }

Clase base para pruebas:

public class BaseModelTest { PipeGameTest pipeGameTest; @Singleton @Component(modules = PipeGameTestModule.class) public interface PipeGameTest { void inject(BoardModelTest boardModelTest); void inject(ConveyorModelTest conveyorModelTest); } @Before public void setUp() { pipeGameTest = DaggerBaseModelTest_PipeGameTest.create(); // Doesn''t work } public PipeGameTest component() { return pipeGameTest; } }

o:

public class BaseModelTest { PipeGameApplication.PipeGame pipeGameTest; // This works if I make the test module extend // the prod module, but it can''t inject my test classes @Before public void setUp() { pipeGameTest = DaggerPipeGameApplication_PipeGame.builder().pipeGameModule(new PipeGameModuleTest()).build(); } public PipeGameApplication.PipeGame component() { return pipeGameTest; } }

Módulo de prueba:

@Module public class PipeGameTestModule { @Provides @Singleton Random provideRandom() { return mock(Random.class); } }


En mi opinión, puede abordar este problema mirándolo desde un ángulo diferente. Fácilmente podrá poner a prueba su clase al no depender de Dagger para la clase de construcción bajo prueba con sus dependencias falsas inyectadas en ella.

Lo que quiero decir es que en la configuración de prueba puedes:

  • Burlarse de las dependencias de la clase bajo prueba
  • Construya la clase bajo prueba manualmente usando las dependencias burladas

No es necesario probar si las dependencias se están inyectando correctamente ya que Dagger verifica la corrección del gráfico de dependencia durante la compilación. Por lo tanto, cualquier error de este tipo se informará por error de compilación. Y es por eso que la creación manual de la clase bajo prueba en el método de configuración debería ser aceptable.

Ejemplo de código donde se inyecta dependencia usando constructor en la clase bajo prueba:

public class BoardModelTest { private BoardModel boardModel; private Random random; @Before public void setUp() { random = mock(Random.class); boardModel = new BoardModel(random); } @Test ... } public class BoardModel { private Random random; @Inject public BoardModel(Random random) { this.random = random; } ... }

Ejemplo de código donde se inyecta dependencia usando el campo en la clase bajo prueba (en caso de que BoardModel esté construido por un marco):

public class BoardModelTest { private BoardModel boardModel; private Random random; @Before public void setUp() { random = mock(Random.class); boardModel = new BoardModel(); boardModel.random = random; } @Test ... } public class BoardModel { @Inject Random random; public BoardModel() {} ... }



Has dado en el clavo diciendo:

El componente de la aplicación no tiene métodos de inyección para mis clases de prueba

Entonces, para evitar este problema, podemos hacer una versión de prueba de su clase de Aplicación. Entonces podemos tener una versión de prueba de su módulo. Y para que todo funcione en una prueba, podemos usar Robolectric.

1) Crea la versión de prueba de tu clase de aplicación

public class TestPipeGameApp extends PipeGameApp { private PipeGameModule pipeGameModule; @Override protected PipeGameModule getPipeGameModule() { if (pipeGameModule == null) { return super.pipeGameModule(); } return pipeGameModule; } public void setPipeGameModule(PipeGameModule pipeGameModule) { this.pipeGameModule = pipeGameModule; initComponent(); }}

2) Su clase de aplicación original debe tener los métodos initComponent () y pipeGameModule ()

public class PipeGameApp extends Application { protected void initComponent() { DaggerPipeGameComponent.builder() .pipeGameModule(getPipeGameModule()) .build(); } protected PipeGameModule pipeGameModule() { return new PipeGameModule(this); }}

3) Tu PipeGameTestModule debería extender el módulo de producción con un constructor:

public class PipeGameTestModule extends PipeGameModule { public PipeGameTestModule(Application app) { super(app); }}

4) Ahora, en el método setup () de su prueba junit, configure este módulo de prueba en su aplicación de prueba:

@Before public void setup() { TestPipeGameApp app = (TestPipeGameApp) RuntimeEnvironment.application; PipeGameTestModule module = new PipeGameTestModule(app); app.setPipeGameModule(module); }

Ahora puedes personalizar tu módulo de prueba como querías originalmente.


Si está utilizando dagger2 con Android, puede usar los sabores de la aplicación para proporcionar recursos de burla.

Vea aquí una demostración de sabores en pruebas simuladas (sin daga): https://www.youtube.com/watch?v=vdasFFfXKOY

Esta base de código tiene un ejemplo: https://github.com/googlecodelabs/android-testing

En su /src/prod/com/yourcompany/Component.java, usted proporciona sus componentes de producción.

En su /src/mock/com/yourcompany/Component.java , proporciona sus componentes de burla.

Esto le permite crear compilaciones de su aplicación con burla o sin ella. También permite el desarrollo paralelo (backend por un equipo, aplicación frontend de otro equipo), puedes simular hasta que los métodos api estén disponibles.

Cómo se ven mis comandos gradle (es un Makefile):

install_mock: ./gradlew installMockDebug install: ./gradlew installProdDebug test_unit: ./gradlew testMockDebugUnitTest test_integration_mock: ./gradlew connectedMockDebugAndroidTest test_integration_prod: ./gradlew connectedProdDebugAndroidTest