java - dependencias - ¿Cómo pruebo las inyecciones de Guice?
dagger android (4)
En mi humilde opinión, no deberías estar probando eso. Los muchachos de Google Guice tienen las pruebas unitarias para afirmar que las inyecciones funcionan como se esperaba, después de todo, eso es lo que Guice está diseñado para hacer. Solo debe escribir pruebas para su propio código (A y B).
Le di a Google Guice la responsabilidad de cablear mis objetos. Pero, ¿cómo puedo probar si las uniones funcionan bien?
Por ejemplo, supongamos que tenemos una clase A
que tiene una dependencia B
¿Cómo puedo probar que B se inyecta correctamente?
class A {
private B b;
public A() {}
@Inject
public void setB(B b) {
this.b = b
}
}
Observe que A
no tiene un método getB()
y quiero afirmar que Ab
no es null
.
No creo que debas probar que se establezcan miembros privados. Es mejor probar contra la interfaz pública de su clase. Si no se inyectó el miembro "b", es probable que obtenga una ExcepciónPobreNivel ejecutando sus pruebas, lo que debería ser una gran advertencia.
Otra forma de probar su configuración es tener un conjunto de pruebas que pruebe su aplicación de principio a fin. Aunque las pruebas de extremo a extremo evalúan nominalmente los casos de uso, verifican indirectamente que su aplicación esté configurada correctamente (que todas las dependencias estén cableadas, etc., etc.). Las pruebas unitarias, por otro lado, deben centrarse exclusivamente en el dominio y no en el contexto en el que se implementa el código.
También estoy de acuerdo con la respuesta de NamshubWriter. No estoy en contra de las pruebas que verifican la configuración, siempre y cuando estén agrupadas en un conjunto de pruebas separado para las pruebas de su unidad.
Para cualquier proyecto complejo de Guice, debe agregar pruebas para asegurarse de que los módulos se puedan usar para crear sus clases. En su ejemplo, si B era un tipo que Guice no podía descifrar cómo crear, entonces Guice no podrá crear A. Si A no era necesario para iniciar el servidor, sino que era necesario cuando su servidor manejaba una solicitud, eso causaría problemas.
En mis proyectos, escribo pruebas para módulos no triviales. Para cada módulo, uso requireBinding() para declarar qué enlaces requiere el módulo pero no define. En mis pruebas, creo un inyector Guice utilizando el módulo bajo prueba y otro módulo que proporciona los enlaces necesarios. Aquí hay un ejemplo usando JUnit4 y JMock:
/** Module that provides LoginService */
public class LoginServiceModule extends AbstractModule {
@Override
protected void configure() {
requireBinding(UserDao.class);
}
@Provides
LoginService provideLoginService(UserDao dao) {
...
}
}
@RunWith(JMock.class)
public class LoginServiceModuleTest {
private final Mockery context = new Mockery();
@Test
public void testModule() {
Injector injector = Guice.createInjector(
new LoginServiceModule(), new ModuleDeps());
// next line will throw an exception if dependencies missing
injector.getProvider(LoginService.class);
}
private class ModuleDeps extends AbstractModule {
private final UserDao fakeUserDao;
public ModuleDeps() {
fakeUserDao = context.mock(UserDao.class);
}
@Override
protected void configure() {}
@Provides
Server provideUserDao() {
return fakeUserDao;
}
}
}
Observe cómo la prueba solo solicita un proveedor. Eso es suficiente para determinar que Guice podría resolver los enlaces. Si LoginService fue creado por un método de proveedor, esta prueba no probaría el código en el método del proveedor.
Esta prueba tampoco prueba que UserDao
lo correcto con UserDao
, o que UserDao
tuvo el alcance correcto. Algunos argumentarían que ese tipo de cosas rara vez vale la pena verificar; si hay un problema, sucede una vez. Deberías "probar hasta que el miedo se convierta en aburrimiento".
Las pruebas de Módulo son útiles porque a menudo agrego nuevos puntos de inyección, y es fácil olvidarse de agregar un enlace.
Las llamadas requireBinding()
pueden ayudar a Guice a capturar los enlaces que faltan antes de devolver el inyector. En el ejemplo anterior, la prueba aún funcionaría si las llamadas requireBinding()
no estuvieran allí, pero me gusta tenerlas porque sirven como documentación.
Para módulos más complicados (como mi módulo raíz), podría usar Modules.override() para anular enlaces que no quiero en el momento de la prueba (por ejemplo, si quiero verificar que se cree mi objeto raíz, probablemente No quiero que cree un objeto que se conectará a la base de datos). Para proyectos simples, es posible que solo pruebe el módulo de nivel superior.
Tenga en cuenta que Guice no inyectará nulos a menos que el campo esté anotado con @Nullable
por lo que muy rara vez necesita verificar que los objetos inyectados no sean nulos en sus pruebas. De hecho, cuando @Inject
constructores con @Inject
, no me molesto en comprobar si los parámetros son null
(de hecho, mis pruebas suelen inyectar null
en el constructor para mantener las pruebas simples).