在Guice中覆盖绑定

tdd*_*key 129 java unit-testing 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的测试中,我只想覆盖InterfaceC,同时保持InterfaceA和InterfaceB,所以我想要像:

Module testModule = new Module() {
    public void configure(Binder binder) {
        binder.bind(InterfaceC.class).to(MockC.class);
    }
};
Guice.createInjector(new ProductionModule(), testModule);
Run Code Online (Sandbox Code Playgroud)

我也试过以下,没有运气:

Module testModule = new ProductionModule() {
    public void configure(Binder binder) {
        super.configure(binder);
        binder.bind(InterfaceC.class).to(MockC.class);
    }
};
Guice.createInjector(testModule);
Run Code Online (Sandbox Code Playgroud)

有谁知道是否有可能做我想做的事情,或者我完全咆哮错误的树?

---跟进:如果我在接口上使用@ImplementedBy标记,然后在测试用例中提供一个绑定,看起来我可以实现我想要的,当它之间存在1-1映射时,它可以很好地工作界面和实现.

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

alb*_*rtb 139

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

另一方面,如果您真的想要替换单个绑定,可以使用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()));
Run Code Online (Sandbox Code Playgroud)

详情请见此处.

但是作为Modules.overrides(..)推荐的javadoc ,您应该以不需要覆盖绑定的方式设计模块.在您给出的示例中,您可以通过将绑定移动InterfaceC到单独的模块来实现.

  • 感谢阿尔伯特,这让我在路上走向了我想做的事情.那是在生产版本中!这是集成测试,而不是单元测试,我为什么要确保正确构建其他所有内容 (8认同)
  • 大小事项.当您的依赖图增长时,手动连接可能会非常痛苦.更换接线时,您需要手动更新所有手动接线位置.覆盖允许您自动处理. (4认同)
  • @pdeschen对于Guice 4来说,这是Guice 3中的一个错误[我修复](https://github.com/google/guice/commit/ebe9f053ab2431f8f22f6d892cffadef4b10479c). (3认同)

Mon*_*ari 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后以这种方式创建您的注入器:

Guice.createInjector(new TestModule());
Run Code Online (Sandbox Code Playgroud)

  • `@Override` 似乎不起作用。特别是如果它是在一个`@Provides` 的方法上完成的。 (3认同)

Jan*_*sen 6

如果您不想更改生产模块并且您有一个默认的类似 Maven 的项目结构,例如

src/test/java/...
src/main/java/...
Run Code Online (Sandbox Code Playgroud)

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