Mocking un campo inyectado en las pruebas unitarias
Tengo una clase Presenter
que utiliza un campo inyectado a través de Dagger, se ve algo así:
public class RssListPresenter { @Inject RssService rssService; // <-- injected field public RssListPresenter() { setupDI(); } private void setupDI() { DaggerNetworkComponent.builder() .networkModule(new NetworkModule()) .build() .inject(this); } public void loadItems() { Rss rss = rssService.getRssFeed() // .... } }
Todo funciona bien. Ahora, me gustaría probar la clase RssListPresenter
. La pregunta es ¿cómo puedo proporcionar un RssService
simulado al presentador?
- Los simulacros de Mockito ejecutan el código actual de Android en Lollipop o superior
- Cómo llamar a Button.performClick en Android JUnit test case?
- Cómo configurar gradle-2.2.1-all.zip manualmente
- Robolectric no utiliza ShadowWebView como la superclase de una clase que extiende WebView. MustOverrideException en su lugar
- PowerMock en el proyecto Android
Ofcourse puedo agregar un nuevo método setRssService(RssService rssService)
al presentador y utilizarlo para proporcionar el simulacro de pruebas de unidad, pero agregar este método sólo para las pruebas de unidad no se siente bien. ¿Cuál sería la manera correcta de manejar esto?
Para mayor completitud aquí están las declaraciones de módulos y componentes:
@Singleton @Component(modules = NetworkModule.class) public interface NetworkComponent { void inject(RssListPresenter presenter); } @Module public class NetworkModule { @Provides Retrofit provideRetrofit() { // ... } @Provides @Singleton RssService providePcWorldRssService(Retrofit retrofit) { return retrofit.create(RssService.class); } }
- IllegalStateException en Android Support al ejecutar pruebas de unidad
- Prueba de unidad de Android con Retrofit y Mockito
- Prueba de unidad de Android: ActivityMonitor waitForActivityWithTimeout devuelve NULL, getActivity nunca devuelve, error de permiso de INJECT_EVENTS
- Deshabilitar el error de clase duplicado para las clases en la carpeta de prueba - Android Studio / IntelliJ
- Android inmediatamente creado Los elementos de par son nulos
- ActivityUnitTestCase y startActivity
- Preguntas sobre la prueba de unidad Android con gradle o estudio de android
- Ninguna prueba encontrada para dado incluye error, al ejecutar la prueba de unidad parametrizada en Android Studio
Inyección de propiedad es como que no es tan fácil de probar. En este caso, la inyección Constructor es mucho mejor. Refactorice su constructor para que se vea así:
private final RssService rssService; @Inject public RssListPresenter(RssService rssService) { this.rssService = rssService; }
Ahora puedes probarlo fácilmente:
//mocks RssService mockRssService; //system under test RssListPresenter rssListPresenter; @Before public void setup() { mockRssService = Mockito.mock(RssService.class); rssListPresenter = new RssListPresenter(mockRssService); }
Probablemente no debería estar usando DaggerNetworkComponent.inject(this)
dentro de RssListPresenter
. En su lugar, debe configurar la daga para que al inyectar miembros en sus clases de nivel superior ( Activity
, Fragment
, Service
), pueda acceder al gráfico de objetos y crear una instancia de su RssPresenter
.
¿Por qué sólo poner inyectores en la Activity
y el Service
y no en algo como RssListPresenter
? Estas son clases que son instanciadas por el sistema Android y por lo que no tiene más remedio que usar inyectores.
Para aclarar, Activity
, Fragment
, etc. son objetivos de inyección ideales. RssListPresenter
etc se inyectan dependencias . Es necesario configurar el marco de inyección de dependencia, dagger, para que pueda proporcionar las dependencias correctas para inyectar en los objetivos de inyección.
Así que también necesitará escribir un método RssListPresenter
para RssListPresenter
@Provides provideRssListPresenter(RssService rssService) { return new RssListPresenteR(rssService); }
Su clase viola algunos de los principios de SOLID
, y esto hace que sea muy difícil para la unidad de prueba. Por supuesto, su clase viola el SRP (Single Responsability Principle) ya que tiene más de una razón para cambiar. También existe una posible violación de Inversión de Dependencia.
Recomiendo repensar el modelo de las clases, para que cada una de ellas desempeñe una función específica, y tenga una razón para cambiar.
- ¿La variable estática es verdaderamente "global" (en todo el sistema) en Android?
- ¿Cómo utilizar actividades o fragmentos en un flujo de trabajo de varias pantallas?