【问题标题】:How do I test Guice injections?如何测试 Guice 注射?
【发布时间】:2010-03-15 14:56:28
【问题描述】:

我让 Google Guice 负责连接我的对象。但是,如何测试绑定是否正常工作?

例如,假设我们有一个类A,它有一个依赖B。如何测试 B 是否正确注入?

class A {
    private B b;
    public A() {}

    @Inject
    public void setB(B b) {
        this.b = b
    }
}

注意A 没有getB() 方法,我想断言A.b 不是null

【问题讨论】:

    标签: java testing dependency-injection guice


    【解决方案1】:

    对于任何复杂的 Guice 项目,您应该添加测试以确保模块可用于创建您的类。在您的示例中,如果 B 是 Guice 无法弄清楚如何创建的类型,那么 Guice 将无法创建 A。如果启动服务器不需要 A,但在您的服务器处理 a 时需要 A请求,这会导致问题。

    在我的项目中,我为重要的模块编写测试。对于每个模块,我使用requireBinding() 来声明模块需要但未定义的绑定。在我的测试中,我使用被测模块和另一个提供所需绑定的模块创建了一个 Guice 注入器。下面是一个使用 JUnit4 和 JMock 的示例:

    /** Module that provides LoginService */
    public class LoginServiceModule extends AbstractModule {
      @Override 
      protected void configure() {
        requireBinding(UserDao.class);
      }
    
      @Provides
      LoginService provideLoginService(UserDao dao) {
        ...
      }
    }
    
    @RunWith(JMock.class)
    public class LoginServiceModuleTest {
      private final Mockery context = new Mockery();
    
      @Test
      public void testModule() {
        Injector injector = Guice.createInjector(
            new LoginServiceModule(), new ModuleDeps());
    
        // next line will throw an exception if dependencies missing
        injector.getProvider(LoginService.class);
      }
    
      private class ModuleDeps extends AbstractModule {
        private final UserDao fakeUserDao;
    
        public ModuleDeps() {
          fakeUserDao = context.mock(UserDao.class);
        }
    
        @Override 
        protected void configure() {}
    
        @Provides
        Server provideUserDao() {
          return fakeUserDao;
        }
      }
    }
    

    请注意测试如何只要求提供者。这足以确定 Guice 可以解决绑定问题。如果 LoginService 是由 provider 方法创建的,则此测试不会测试 provider 方法中的代码。

    此测试也不会测试您是否将正确的东西绑定到UserDao,或者UserDao 的范围是否正确。有些人会争辩说,这些类型的东西很少值得检查。如果有问题,它会发生一次。你应该“测试直到恐惧变成无聊。”

    我发现模块测试很有用,因为我经常添加新的注入点,而且很容易忘记添加绑定。

    requireBinding() 调用可以帮助 Guice 在返回注入器之前捕获丢失的绑定!在上面的示例中,如果没有 requireBinding() 调用,测试仍然可以工作,但我喜欢使用它们,因为它们用作文档。

    对于更复杂的模块(比如我的根模块),我可能会使用Modules.override() 来覆盖我在测试时不想要的绑定(例如,如果我想验证我的根对象是否被创建,我可能不希望它创建将连接到数据库的对象)。对于简单的项目,您可能只测试顶级模块。

    请注意,Guice will not inject nulls 除非该字段使用 @Nullable 注释,因此您很少需要在测试中验证注入的对象是否非空。事实上,当我用@Inject 注释构造函数时,我不会费心检查参数是否为null(事实上,我的测试经常将null 注入构造函数以保持测试简单)。

    【讨论】:

    • 非常感谢。你的论点很清楚。我有在部署应用程序后未创建的依赖项
    • my tests often inject null into the constructor to keep the tests simple => 这可能表明您的班级可能没有凝聚力
    • @beluchin 你能解释一下你的意思吗?我尽量避免在我的构造函数中做“真正的工作”,所以将null 传递给构造函数很少有问题。如果我正在测试一个类,并且我正在测试的方法不使用其中一个字段,那么为该构造函数参数传递一个null 是最简单的事情。如果这不起作用,我会注入一个模拟对象(用于服务)或一个真实实例(用于值对象),但其中任何一个都会使代码比我刚刚传入 null 时更复杂
    • @NamshubWriter 定义strong内聚的一种方法是所有公共方法最终引用所有类级别的变量。因此,您可以在测试中将 null 依赖项传递给构造函数,因为您在测试中调用的公共方法不引用依赖项 - 根据定义,缺乏内聚力。
    • @MarkD 在我的解决方案中,BUserDAO,并且测试无法验证它是否可以创建。该模块保证如果您将LoginServiceModule 传递给Guice.create injector() 并且Guice 没有创建UserDao 所需的绑定,createInjector 将抛出异常
    【解决方案2】:

    另一种测试配置的方法是使用测试套件来测试您的应用程序端到端。尽管端到端测试名义上是测试用例,但它们会间接检查您的应用程序是否配置正确(所有依赖项是否已连接等)。另一方面,单元测试应该只关注领域,而不是部署代码的上下文。

    我也同意 NamshubWriter 的回答。我不反对检查配置的测试,只要它们被分组在一个单独的测试套件中进行单元测试。

    【讨论】:

      【解决方案3】:

      恕我直言,您不应该对此进行测试。 Google Guice 的家伙有单元测试来断言注入按预期工作 - 毕竟,这就是 Guice 的设计目的。您应该只为自己的代码(A 和 B)编写测试。

      【讨论】:

      • 我认为您可能需要多了解 DI。关键是 DI 框架“将”产生构建实例,并将在适当的地方为您注入它们(这就是创建它们的原因)——您将分离您的创建和业务逻辑。现在您可以单独测试它们。然而,测试您的创建代码并不意味着要确保 Guice 按预期工作。如果你不信任这个库,也许你应该使用别的东西。
      • Guice 的人不同意你的说法。引用:“如果您的提供程序很复杂,请务必对其进行测试!” (code.google.com/p/google-guice/wiki/ProviderBindings)
      • @Joe23 - 测试 Guice 提供者和测试 Guice 绑定是两件不同的事情。
      • 我不同意——Guice 配置中的错误会在运行时出现,并有可能使您的软件完全不可用,而不仅仅是运行时出现缺陷。您肯定希望确保它按预期工作,即使为这些情况编写测试需要一些额外的计划和考虑。我宁愿编写错误的逻辑,最终导致注入测试失败,并在我的笔记本电脑上了解它,而不是通过 CI 推送一些粗略的东西并浪费我队友的时间——或者取决于部署工具的成熟度,更糟糕的是.
      【解决方案4】:

      我认为您不应该测试设置的私有成员。最好针对您的类的公共接口进行测试。如果不注入成员“b”,您可能会在执行测试时收到 NullPointerException,这应该是很多警告。

      【讨论】:

        猜你喜欢
        • 2012-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-05
        • 1970-01-01
        • 1970-01-01
        • 2012-12-29
        相关资源
        最近更新 更多