威胁中的约束
我刚开始玩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
,它的工作完美无瑕。