威胁中的约束

我刚开始玩Guice,我能想到的一个用例是,在一个testing中我只想重载一个绑定。 我想我想使用其余的生产级别的绑定,以确保一切安装正确,并避免重复。

所以想象我有以下模块

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

而在我的testing中,我只想重写InterfaceC,同时保持InterfaceA和InterfaceB的机智,所以我想要的东西是这样的:

 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标记,然后在testing用例中提供一个绑定,看起来我可以达到我想要的效果,界面和实现。

另外,在和一位同事讨论这个问题之后,似乎我们会走上覆盖整个模块的道路,并确保我们的模块能够正确定义。 这似乎可能会导致一个问题,虽然在一个模块中绑定错位,需要移动,因此可能会打破一个testing的负载,因为绑定可能不再可用被重写。

这可能不是你正在寻找的答案,但是如果你正在编写unit testing,那么你可能不应该使用注入器,而应该手工注入模拟或伪造的对象。

另一方面,如果你真的想replace一个绑定,你可以使用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推荐,你应该devise你的模块,你不需要重写绑定。 在你给的例子中,你可以通过将InterfaceC的绑定移动到一个单独的模块来实现。

为什么不使用inheritance? 您可以覆盖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); } } 

最后用这种方法创build你的注射器:

 Guice.createInjector(new TestModule()); 

如果你不想改变你的生产模块,并且你有一个默认的类似maven的项目结构

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

您可以使用与原始类相同的包,在您的testing目录中创build一个新的类ConcreteC 。 Guice然后将InterfaceC绑定到您的testing目录中的ConcreteC ,而所有其他接口将绑定到您的生产类。

你想使用Juckito ,你可以为每个testing类声明你的自定义configuration。

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

在不同的设置中,我们在单独的模块中定义了多个活动。 被注入的活动位于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> 

然后我们有一个types被注入:

 interface Foo { } 

Foo的一些默认实现:

 class FooThing implements Foo { } 

MainModuleconfigurationFoo的FooThing实现:

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

最后,一个消耗Foo的Activity:

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

在使用Android应用程序模块时,我们想使用SomeActivity但为了testing目的,注入我们自己的Foo

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

有人可能会说模块处理到客户端应用程序,但是,我们需要隐藏所注入的组件,因为库模块是一个SDK,并且揭露块具有更大的含义。

(请记住,这是为了testing,所以我们知道SomeActivity的内部,并知道它消耗(包可见)Foo)。

我发现作品是有道理的, 使用build议的覆盖进行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启动时,它将为其注入的Foo实例获得OtherFooThing

这是一个非常具体的情况,在我们的例子中,OtherFooThing被用于内部loggingtesting情况,而FooThing被默认用于所有其他用途。

请记住,我们在unit testing使用了#newDefaultRoboModule ,它的工作完美无瑕。