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() {}
...
}
Esto es actualmente imposible con Dagger 2 (a partir de v2.0.0) sin algunas soluciones. Puedes leer sobre esto here .
Más sobre posibles soluciones:
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