java unit-testing guice

java - Anulación primordial en Guice



unit-testing (5)

¿Por qué no usar la herencia? Puede anular sus enlaces específicos en el método overrideMe , dejando las implementaciones compartidas en el método de configure .

public class DevModule implements Module { public void configure(Binder binder) { binder.bind(InterfaceA.class).to(TestDevImplA.class); overrideMe(binder); } protected void overrideMe(Binder binder){ binder.bind(InterfaceC.class).to(ConcreteC.class); } }; public class TestModule extends DevModule { @Override public void overrideMe(Binder binder) { binder.bind(InterfaceC.class).to(MockC.class); } }

Y finalmente crea tu inyector de esta manera:

Guice.createInjector(new TestModule());

Acabo de empezar a jugar con Guice, y un caso de uso en el que puedo pensar es que en una prueba solo quiero anular un enlace único. Creo que me gustaría usar el resto de los enlaces de nivel de producción para garantizar que todo esté configurado correctamente y para evitar la duplicación.

Entonces imagina que tengo el siguiente Módulo

public class ProductionModule implements Module { public void configure(Binder binder) { binder.bind(InterfaceA.class).to(ConcreteA.class); binder.bind(InterfaceB.class).to(ConcreteB.class); binder.bind(InterfaceC.class).to(ConcreteC.class); } }

Y en mi prueba, solo quiero anular InterfaceC, mientras mantengo intactos InterfaceA e InterfaceB, así que me gustaría algo como:

Module testModule = new Module() { public void configure(Binder binder) { binder.bind(InterfaceC.class).to(MockC.class); } }; Guice.createInjector(new ProductionModule(), testModule);

También intenté lo siguiente, sin suerte:

Module testModule = new ProductionModule() { public void configure(Binder binder) { super.configure(binder); binder.bind(InterfaceC.class).to(MockC.class); } }; Guice.createInjector(testModule);

¿Alguien sabe si es posible hacer lo que quiero o estoy ladrando completamente el árbol equivocado?

--- Seguimiento: parece que puedo lograr lo que quiero si uso la etiqueta @ImplementedBy en la interfaz y luego solo proporciono un enlace en el caso de prueba, que funciona bien cuando hay un mapeo 1-1 entre la interfaz y la implementación.

Además, después de discutir esto con un colega, parece que nos encaminamos por el camino de anular todo un módulo y asegurarnos de que tenemos nuestros módulos definidos correctamente. Esto parece que podría causar un problema, aunque un enlace se extravía en un módulo y necesita ser movido, por lo tanto, posiblemente se rompa una carga de pruebas ya que los enlaces ya no estarán disponibles para ser reemplazados.


Desea usar Juckito donde puede declarar su configuración personalizada para cada clase de prueba.

@RunWith(JukitoRunner.class) class LogicTest { public static class Module extends JukitoModule { @Override protected void configureTest() { bind(InterfaceC.class).to(MockC.class); } } @Inject private InterfaceC logic; @Test public testLogicUsingMock() { logic.foo(); } }


En una configuración diferente, tenemos más de una actividad definida en módulos separados. La actividad en la que se está inyectando está en un Módulo de biblioteca de Android, con su propia definición de módulo RoboGuice en el archivo AndroidManifest.xml.

La configuración se ve así. En el Módulo de Biblioteca hay estas definiciones:

AndroidManifest.xml:

<application android:allowBackup="true"> <activity android:name="com.example.SomeActivity/> <meta-data android:name="roboguice.modules" android:value="com.example.MainModule" /> </application>

Entonces tenemos un tipo que se inyecta:

interface Foo { }

Alguna implementación predeterminada de Foo:

class FooThing implements Foo { }

MainModule configura la implementación de FooThing para Foo:

public class MainModule extends AbstractModule { @Override protected void configure() { bind(Foo.class).to(FooThing.class); } }

Y finalmente, una actividad que consume Foo:

public class SomeActivity extends RoboActivity { @Inject private Foo foo; }

En el módulo de aplicación de Android que consume, nos gustaría utilizar SomeActivity pero, para fines de prueba, inyectar nuestro propio Foo .

public class SomeOtherActivity extends Activity { @Override protected void onResume() { super.onResume(); Intent intent = new Intent(this, SomeActivity.class); startActivity(intent); } }

Uno podría argumentar para exponer el manejo del módulo a la aplicación del cliente, sin embargo, tenemos que ocultar en su mayoría los componentes que se inyectan porque el Módulo de biblioteca es un SDK, y exponer piezas tiene implicaciones más grandes.

(Recuerde, esto es para probar, así que conocemos las partes internas de SomeActivity, y sabemos que consume un (paquete visible) Foo).

La forma en que encontré que las obras tiene sentido; use la anulación sugerida para probar :

public class SomeOtherActivity extends Activity { private class OverrideModule extends AbstractModule { @Override protected void configure() { bind(Foo.class).to(OtherFooThing.class); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RoboGuice.overrideApplicationInjector( getApplication(), RoboGuice.newDefaultRoboModule(getApplication()), Modules .override(new MainModule()) .with(new OverrideModule())); } @Override protected void onResume() { super.onResume(); Intent intent = new Intent(this, SomeActivity.class); startActivity(intent); } }

Ahora, cuando se inicia SomeActivity , obtendrá OtherFooThing para su instancia de Foo inyectada.

Es una situación muy específica en la que, en nuestro caso, se usó OtherFooThing internamente para registrar situaciones de prueba, mientras que FooThing se usó, por defecto, para todos los demás usos.

Tenga en cuenta que estamos usando #newDefaultRoboModule en nuestras pruebas unitarias, y funciona sin problemas.


Puede que esta no sea la respuesta que estás buscando, pero si estás escribiendo pruebas unitarias, probablemente no deberías usar un inyector y estar inyectando objetos simulados o falsos a mano.

Por otro lado, si realmente desea reemplazar un enlace único, puede usar Modules.override(..) :

public class ProductionModule implements Module { public void configure(Binder binder) { binder.bind(InterfaceA.class).to(ConcreteA.class); binder.bind(InterfaceB.class).to(ConcreteB.class); binder.bind(InterfaceC.class).to(ConcreteC.class); } } public class TestModule implements Module { public void configure(Binder binder) { binder.bind(InterfaceC.class).to(MockC.class); } } Guice.createInjector(Modules.override(new ProductionModule()).with(new TestModule()));

Ver detalles here .

Pero como recomienda el javadoc para Modules.overrides(..) , debe diseñar sus módulos de tal forma que no necesite anular los enlaces. En el ejemplo que proporcionó, podría lograr eso moviendo la unión de InterfaceC a un módulo separado.


Si no desea cambiar su módulo de producción y si tiene una estructura de proyecto similar a maven predeterminada como

src/test/java/... src/main/java/...

Simplemente puede crear una nueva clase ConcreteC en su directorio de prueba usando el mismo paquete que para su clase original. Guice enlazará entonces InterfaceC con ConcreteC desde su directorio de prueba, mientras que todas las demás interfaces estarán vinculadas a sus clases de producción.