Question

Je viens de commencer à jouer avec Guice, et un cas d’utilisation auquel je peux penser est que, dans un test, je veux juste écraser une seule liaison. Je pense que je voudrais utiliser le reste des liaisons de niveau de production pour m'assurer que tout est configuré correctement et éviter les doublons.

Alors imaginez que j'ai le module suivant

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);
    }
}

Et dans mon test, je souhaite uniquement ignorer InterfaceC, tout en maintenant InterfaceA et InterfaceB intacts, donc j'aimerais quelque chose comme:

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

J'ai également essayé ce qui suit, sans succès:

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

Est-ce que quelqu'un sait s'il est possible de faire ce que je veux ou est-ce que j'aboie complètement le mauvais arbre?

--- Suivi: Il semblerait que je puisse réaliser ce que je veux si j'utilise la balise @ImplementedBy sur l'interface et que je fournis ensuite une liaison dans le scénario de test, ce qui fonctionne bien lorsqu'il existe un mappage 1-1 entre l'interface et l'implémentation.

De plus, après en avoir discuté avec un collègue, il semblerait que nous nous dirigerions dans la voie de la substitution d'un module entier et de la définition correcte de nos modules. Cela semble poser un problème, même si une liaison est mal placée dans un module et doit être déplacée. Par conséquent, il est possible que la charge de tests soit interrompue, car les liaisons risquent de ne plus être disponibles pour être écrasées.

Était-ce utile?

La solution

Ce n'est peut-être pas la réponse que vous cherchez, mais si vous écrivez des tests unitaires, vous ne devriez probablement pas utiliser d'injecteur mais plutôt injecter des objets factices ou factices à la main.

D'autre part, si vous voulez vraiment remplacer une seule liaison, vous pouvez utiliser 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()));

Voir les détails ici .

Mais comme le recommande javadoc pour Modules.overrides(..), vous devez concevoir vos modules de manière à ne pas avoir à remplacer les liaisons. Dans l'exemple que vous avez donné, vous pouvez accomplir cela en déplaçant la liaison de InterfaceC vers un module séparé.

Autres conseils

Pourquoi ne pas utiliser l'héritage? Vous pouvez remplacer vos liaisons spécifiques dans la méthode overrideMe, en laissant les implémentations partagées dans la configure méthode.

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);
    }
}

Et créez enfin votre injecteur de cette façon:

Guice.createInjector(new TestModule());

Si vous ne souhaitez pas modifier votre module de production et si vous avez une structure de projet par défaut de type maven, telle que

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

Vous pouvez simplement créer une nouvelle classe ConcreteC dans votre répertoire de test en utilisant le même package que pour votre classe d'origine. Guice liera ensuite InterfaceC à <=> à partir de votre répertoire de test, tandis que toutes les autres interfaces seront liées à vos classes de production.

Vous souhaitez utiliser Juckito pour déclarer votre configuration personnalisée pour chaque classe de test.

@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();
    }
}

Dans une configuration différente, plusieurs activités sont définies dans des modules distincts. L'activité injectée se trouve dans un module de bibliothèque Android, avec sa propre définition de module RoboGuice dans le fichier AndroidManifest.xml.

La configuration ressemble à ceci. Le module Bibliothèque contient les définitions suivantes:

AndroidManifest.xml:

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

Nous avons ensuite un type injecté:

interface Foo { }

Certaines implémentations par défaut de Foo:

class FooThing implements Foo { }

MainModule configure l'implémentation FooThing pour Foo:

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

Et enfin, une activité qui consomme Foo:

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

Dans l'Android Application Module, nous aimerions utiliser SomeActivity mais, à des fins de test, injecter notre propre Foo.

public class SomeOtherActivity extends Activity {
    @Override
    protected void onResume() {
        super.onResume();

        Intent intent = new Intent(this, SomeActivity.class);
        startActivity(intent);
    }
}

On pourrait argumenter pour exposer la gestion du module à l’application cliente, mais nous devons surtout masquer les composants injectés car le module de bibliothèque est un SDK, et l’exposition de pièces a de plus grandes implications.

(N'oubliez pas que c'est pour les tests, nous connaissons donc les composants internes de SomeActivity et savons qu'il consomme un Foo (package visible)).

Ce que j'ai trouvé qui fonctionne est logique; utilisez le remplacement suggéré pour tester :

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);
    }
}

Maintenant, quand OtherFooThing est lancé, il obtiendra #newDefaultRoboModule pour son instance <=> injectée.

Il s'agit d'une situation très spécifique où, dans notre cas, OtherFooThing était utilisé en interne pour enregistrer des situations de test, tandis que FooThing était utilisé, par défaut, pour toutes les autres utilisations.

N'oubliez pas que nous utilisons des <=> dans nos tests unitaires, et cela fonctionne parfaitement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top