154

私は Guice を使い始めたばかりですが、考えられるユースケースは、テストで単一のバインディングをオーバーライドしたいということです。すべてが正しくセットアップされていることを確認し、重複を避けるために、残りのプロダクション レベルのバインディングを使用したいと考えています。

次のモジュールがあると想像してください

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

私のテストでは、InterfaceA と InterfaceB をそのまま維持しながら、InterfaceC のみをオーバーライドしたいので、次のようなものが必要です。

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

私はまた、次のことを試しましたが、うまくいきませんでした:

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

私がやりたいことができるのか、それとも間違ったツリーを完全に吠えているのか、誰かが知っていますか??

--- フォローアップ: インターフェイスで @ImplementedBy タグを使用し、テスト ケースでバインディングを提供するだけで、目的を達成できるように思われます。インターフェイスと実装。

また、これについて同僚と話し合った後、モジュール全体をオーバーライドし、モジュールが正しく定義されていることを確認する道に進むように思われます。これは、バインディングがモジュール内で誤って配置され、移動する必要がある場合に問題を引き起こす可能性があるようです。そのため、バインディングをオーバーライドすることができなくなるため、テストの負荷が壊れる可能性があります。

4

5 に答える 5

164

これはあなたが探している答えではないかもしれませんが、単体テストを書いている場合は、おそらくインジェクターを使用するのではなく、モックまたは偽のオブジェクトを手で注入する必要があります。

一方、本当に単一のバインディングを置き換えたい場合は、次を使用できます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()));

詳細はこちらをご覧ください。

ただし、javadocでModules.overrides(..)推奨されているように、バインディングをオーバーライドする必要がないようにモジュールを設計する必要があります。InterfaceC与えた例では、のバインディングを別のモジュールに移動することでそれを実現できます。

于 2009-02-10T05:04:02.503 に答える
12

なぜ継承を使用しないのですか? メソッド内の特定のバインディングをオーバーライドしてoverrideMe、共有実装を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);
    }
}

最後に、この方法でインジェクターを作成します。

Guice.createInjector(new TestModule());
于 2014-08-27T13:19:13.253 に答える
5

本番モジュールを変更したくない場合、および次のようなデフォルトの Maven のようなプロジェクト構造がある場合

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

ConcreteC元のクラスと同じパッケージを使用して、テスト ディレクトリに新しいクラスを作成するだけです。その後、Guice はテスト ディレクトリからバインドInterfaceCされますが、他のすべてのインターフェイスはプロダクション クラスにバインドされます。ConcreteC

于 2015-01-06T16:34:16.113 に答える
1

各テストクラスのカスタム構成を宣言できるJuckitoを使用したいと考えています。

@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();
    }
}
于 2014-11-11T11:35:03.440 に答える
1

別の設定では、個別のモジュールで定義された複数のアクティビティがあります。注入されるアクティビティは、Android ライブラリ モジュール内にあり、AndroidManifest.xml ファイル内に独自の RoboGuice モジュール定義があります。

セットアップはこんな感じ。ライブラリ モジュールには、次の定義があります。

AndroidManifest.xml:

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

次に、タイプが注入されます。

interface Foo { }

Foo のデフォルトの実装:

class FooThing implements Foo { }

MainModule は、Foo の FooThing 実装を構成します。

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

そして最後に、Foo を消費するアクティビティ:

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

消費する Android アプリケーション モジュールで、使用したいのですSomeActivityが、テスト目的で、独自のFoo.

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

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

モジュール処理をクライアント アプリケーションに公開することを主張する人もいるかもしれませんが、ライブラリ モジュールは SDK であり、一部を公開することはより大きな影響を与えるため、挿入されるコンポーネントをほとんど非表示にする必要があります。

(これはテスト用であるため、SomeActivity の内部構造はわかっており、(パッケージが表示される) Foo を消費することがわかっていることを思い出してください)。

私が見つけた方法は理にかなっています。テスト用に提案されたオーバーライドを使用します。

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

これで、SomeActivityが開始されるとOtherFooThing、注入されたFooインスタンスが取得されます。

私たちの場合、OtherFooThing はテスト状況を記録するために内部的に使用されましたが、FooThing はデフォルトで他のすべての用途に使用されたという非常に特殊な状況です。

単体テストで使用していることを覚えておいてください。問題なく動作します。#newDefaultRoboModule

于 2015-04-27T17:10:20.177 に答える