私は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);
誰かが私がやりたいことができるのか、間違った木を完全にcompletelyえているのかを知っていますか?
---フォローアップ:インターフェイスで@ImplementedByタグを使用し、テストケースでバインディングを提供するだけで、目的を達成できるように見えます。インターフェースと実装。
また、同僚とこれについて議論した後、モジュール全体をオーバーライドし、モジュールを正しく定義することを確実にする道を進んでいくように思われます。これは、バインディングがモジュール内で誤って配置され、移動する必要がある場合に問題を引き起こす可能性があるように見えます。
これはあなたが探している答えではないかもしれませんが、ユニットテストを書いているなら、おそらくインジェクターを使うべきではなく、モックや偽のオブジェクトを手で注入するべきです。
一方、単一のバインディングを本当に置き換えたい場合は、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()));
_
詳細を参照してください こちら 。
ただし、Modules.overrides(..)
のjavadocが推奨しているように、バインディングをオーバーライドする必要がないようにモジュールを設計する必要があります。あなたが与えた例では、InterfaceC
のバインディングを別のモジュールに移動することでそれを達成できます。
なぜ継承を使用しないのですか? 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());
プロダクションモジュールを変更したくない場合、およびデフォルトのMavenのようなプロジェクト構造がある場合
src/test/Java/...
src/main/Java/...
元のクラスと同じパッケージを使用して、テストディレクトリに新しいクラスConcreteC
を作成するだけです。 GuiceはテストディレクトリからInterfaceC
をConcreteC
にバインドしますが、他のすべてのインターフェイスは実稼働クラスにバインドされます。
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();
}
}
別のセットアップでは、別々のモジュールで複数のアクティビティが定義されています。挿入されるアクティビティは、AndroidManifest.xmlファイルに独自のRoboGuiceモジュール定義を持つAndroid Library Moduleにあります。
セットアップは次のようになります。ライブラリモジュールには、次の定義があります。
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 Application Module)では、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を消費することがわかります)。
私が見つけた方法は理にかなっています。 testingに推奨されるオーバーライドを使用します。
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は他のすべての用途にデフォルトで使用された非常に特殊な状況です。
areを使用して#newDefaultRoboModule
単体テストでは、問題なく動作します。