在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映射时,它可以很好地工作界面和实现。

此外,在与同事讨论这个问题后,我们似乎会超越整个模块,并确保正确定义模块。这似乎可能会导致问题,虽然绑定在模块中放错位置并需要移动,因此可能会破坏测试的负载,因为绑定可能不再可用于覆盖。


#1 热门回答(126 赞)

这可能不是你正在寻找的答案,但如果你正在编写单元测试,你可能不应该使用注入器而是手动注入模拟或伪造对象。

另一方面,如果你真的想要替换单个绑定,可以使用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 forModules.overrides(..)recommends,你应该设计你的模块,使你不需要覆盖绑定。在你给出的示例中,你可以通过将InterfaceC的绑定移动到单独的模块来实现此目的。


#2 热门回答(9 赞)

为什么不使用继承?你可以在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());

#3 热门回答(3 赞)

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

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

你可以使用与原始类相同的包在测试目录中创建新的classConcreteC。然后Guice将从你的测试目录绑定InterfaceCConcreteC,而所有其他接口将绑定到你的生产类。