Guice中的覆盖绑定
我刚刚开始玩Guice,我可以想到的一个用例是,在测试中我只想覆盖单个绑定。我想我想使用剩余的生产级绑定来确保一切安装正确并避免重复。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);
}
}
而在我的测试中,我只想要覆盖但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标记,然后在测试用例中提供一个绑定,当出现1-1接口与实现之间的映射。
另外,在与一位同事讨论这件事后,我们似乎会走上重写整个模块的道路,并确保我们的模块已正确定义。这似乎可能会导致问题,但是绑定在模块中放置错误并且需要移动,因此可能会破坏大量测试,因为绑定可能不再可用于重写。
这可能不是您正在寻找的答案,但是如果您正在编写单元测试,那么您可能不应该使用注入器,而应该手动注入模拟对象或假对象。
在另一方面,如果你真的想更换一个单一的结合,你可以使用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()));
查看详情here。
但作为javadoc Modules.overrides(..)
建议,你应该设计你的模块,你不需要重写绑定。在您给出的示例中,您可以通过将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());
你想用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();
}
}
如果你不想改变你的生产模块,如果你有一个默认的Maven样的项目结构像
src/test/java/...
src/main/java/...
您可以在测试目录使用只需要创建一个新的类ConcreteC
与原始课程相同的包装。然后Guice将绑定InterfaceC
到ConcreteC
从您的测试目录,而所有其他接口将绑定到您的生产类。
在不同的设置中,我们在单独的模块中定义了多个活动。被注入的活动位于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);
}
}
最后,消耗符的活动:
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
,它的工作完美无瑕。
就像“咆哮错误的树”一样:D – 2009-01-27 14:34:48