Android molesta una Dagger2 inyecta dependencia para una prueba de Espresso

Tengo una aplicación fuertemente dependida inyectada ( dagger2 ). Me gustaría ejecutar una prueba de espresso sin tener la prueba de navegar a través de toda la aplicación, e iniciar sesión en la aplicación.

Me gustaría empezar en mi teleActividad, y burlarse del administrador de inicio de sesión. Sin embargo, en cualquier función @test, ya hemos golpeado el puntero nulo como hemos llamado onCreate. Si lo anulamos antes de iniciar la actividad (se muestra a continuación), la actividad es nula.

A mi entender, la capacidad de cambiar nuestras dependencias subrayar es una gran razón por la que utilizamos Dagger2, de lo contrario sería sólo un muy sobre ingeniería singleton. ¿Cómo puedo anular, burlar o cambiar la inyección a un módulo de daga de prueba – para que pueda crear esta sencilla prueba de espresso.

Nota También escribí todo esto en el patrón de diseño MVP si eso hace una diferencia.

Teleactividad

@Inject TelePresenter mTelePresenter; @Inject public LoginStateManager mLoginStateManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ButterKnife.bind(this); DaggerInjectorTele.get().inject(this); mTelePresenter.setTeleDependencies(this); Intent intent = getIntent(); String searchId = null; if (intent != null) { searchId = intent.getStringExtra(Constants.SEARCH_ID); } mTelePresenter.onCreateEvent(searchId, Helper.makeAuthorizationHeader( // CRASH Null pointer mLoginStateManager.getBaseLoginResponse().getAccessToken())); } 

Café exprés

 @LargeTest @RunWith(AndroidJUnit4.class) public class TeleTest { @Rule public ActivityTestRule<TeleActivity> mActivityTestRule = new ActivityTestRule( TeleActivity.class) { @Override protected void beforeActivityLaunched() { super.beforeActivityLaunched(); TeleActivity teleActivity = (TeleActivity)getActivity(); //teleActivity NULL! teleActivity.mLoginStateManager = mock(LoginStateManager.class); LoginResponse loginResponse = mock(LoginResponse.class); when(loginResponse.getAccessToken()).thenReturn("1234"); // Nope here still null when(teleActivity.mLoginStateManager.getBaseLoginResponse()).thenReturn(loginResponse); } }; 

Inyector de Dagger

  public class DaggerInjectorTele { private static TelePresenterComponent telePresenterComponent = DaggerTelePresenterComponent.builder().build(); public static TelePresenterComponent get() { return telePresenterComponent; } } 

TelePresenterComponent

 @Singleton @Component(modules = {TelePresenterModule.class, LoginStateManagerModule.class}) public interface TelePresenterComponent { void inject(TeleActivity activity); } 

TelePresenterModule

 @Module public class TelePresenterModule { @Provides @Singleton public TelePresenter getTelePresenter() { return new TelePresenter(); } } 

LoginStateManagerModule

 @Module public class LoginStateManagerModule { @Provides @Singleton public LoginStateManager getLoginStateManager(){ return new LoginStateManager(); } } 

En primer lugar, su decisión de utilizar la inyección de dependencia (Dagger2) es muy buena y de hecho hará que sus pruebas más fáciles de escribir.

Tienes que anular la configuración de inyección de dependencia (módulo) e inyectar un simulacro. Aquí hay un ejemplo sencillo de cómo se puede hacer.

Primero necesitas un simulacro:

 LoginStateManager lsmMock = mock(LoginStateManager.class); 

Ahora anule la configuración DI para usar este simulacro:

 //Extend your TelePresenterModule, override provider method public class TestTelePresenterModule extends TelePresenterModule{ @Override public LoginStateManager getLoginStateManager() { //simply return the mock here return lsmMock; } } 

Ahora a la prueba:

 @Test //this is an espresso test public void withAMock() { //build a new Dagger2 component using the test override TelePresenterComponent componentWithOverride = DaggerTelePresenterComponent.builder() //mind the Test in the class name, see a class above .telePresenterModule(new TestTelePresenterModule()) .build(); //now we initialize the dependency injector with this new config DaggerInjectorTele.set(componentWithOverride); mActivityRule.launchActivity(null); //verify that injected mock was interacted with verify(lsmMock).whatever(); } 

Ejemplo de: https://github.com/yuriykulikov/DIComparison/blob/master/app/src/androidTest/java/com/example/yuriy/dependencyinjectioncomparison/Dagger2Test.java

Parece problema de arquitectura en lugar de un problema menor.

En primer lugar no crearía una clase estática para llamar dagger2 componente mi enfoque sería más android centric, me refiero a la aplicación singleton con todas sus campanas y silbatos.

De todos modos … La mejor manera de ejecutar pruebas sin ejecutar todo el flujo de trabajo es separar su proyecto en dos proyectos diferentes:

1- Aplicación de interfaz de usuario, tus actividades y fragmentos de Android, etc.

2-Logic Module utilizando una arquitectura de la empresa decir MVP / MVC / MVVM (debe ser un proyecto diferente dentro de su estudio de Android)

¿Dónde debe usar daga? Dentro de su aplicación de interfaz de usuario para pegar el módulo de lógica en su interfaz de usuario.

¿Cómo puedes probar diferentes partes de la aplicación (Logic Module)? Ya que separó su lógica en diferentes escrituras de escritura de parte para ellos sería mucho más fácil, aunque usted no va a necesitar Esperesso más. Simple unidad de pruebas con JUnit y Mockito puede ayudarle sin ejecutar todo el flujo de trabajo.

Tenga en cuenta que no debe tener ningún tipo de lógica dentro de su aplicación de interfaz de usuario.

Mi opinión es Clean Architecture: https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

Tengo un andamio simple para el acercamiento anterior en mi github usted puede leer eso también, bien si usted tiene gusto: https://github.com/vahidhashemi/android_clean_architecture

No hay valor establecido en

LoginStateManager

Por lo que al construir el componente obtendrá la dependencia de TelePresenter y la dependencia de LoginStateManager, pero no los valores establecidos en las variables miembro de ambos. Así que creo que necesita establecer los valores de las variables miembro antes de acceder a ellos.

 getBaseLoginResponse().getAccessToken()) 

La línea de código anterior le da nulo porque no ha establecido el valor. Así que antes de acceder a él, debe establecer los valores primero

  • TDD en Android con Robolectric y Mockito
  • ¿Cómo usar Mockito para probar las API a lo largo de Robospice y Retrofit?
  • Llamadas de método de burla usando power mockito - org.powermock.api.mockito.ClassNotPreparedException
  • Unidad de prueba de la aplicación android con retrofit y rxjava
  • Mocking SQLite-Database durante la prueba de actividad con Robolectric
  • Configurar gradle para usar JUnit, Mockito, Hamcrest y Espresso
  • Prueba de unidad Retrofit api call con Mockito - ArgumentCaptor
  • PowerMock en el proyecto Android
  • Roboguice y simulacros: ¿Cómo roboguice inyectar un servicio de simulacro cuando la prueba, pero utiliza el REAL de lo contrario?
  • Intento de mockito de simular cualquier clase genera ExceptionInInitializerError
  • Junit / Mockito - esperar la ejecución del método
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.