【问题标题】:How do I substitute Guice modules with fake test modules for unit tests?如何用伪造的测试模块替换 Guice 模块来进行单元测试?
【发布时间】:2015-08-04 16:43:18
【问题描述】:

以下是我们在新应用程序中使用 Guice 的方式:

public class ObjectFactory {
  private static final ObjectFactory instance = new ObjectFactory();
  private final Injector injector;

  private ObjectFactory() throws RuntimeException {
    this.injector = Guice.createInjector(new Module1());
  }

  public static final ObjectFactory getInstance() {
    return instance;
  }

  public TaskExecutor getTaskExecutor() {
    return injector.getInstance(TaskExecutor.class);
  }
}

Module1 定义了TaskExecutor 的构造方式。

在代码中我们使用ObjectFactory.getInstance().getTaskExecutor()来获取和TaskExecutor的实例。

在单元测试中,我们希望能够用FakeTaskExecutor 替换它,本质上我们希望在调用ObjectFactory.getInstance().getTaskExecutor() 时获得FakeTaskExecutor 的实例。

我正在考虑实现一个FakeModule,注入器将使用它而不是Module1

在 Spring 中,我们只需使用 @Autowired 注释,然后为 TestProduction 代码定义单独的 bean,并使用 Spring4JunitRunner 运行我们的测试;我们正在尝试对 Guice 做类似的事情。

【问题讨论】:

  • 你研究过嘲笑吗? Mockito 是我推荐的 API。
  • FakeTaskExecutor 可以是模拟或测试替身,我不知道如何拦截ObjectFactory 内的注入器。使用 PowerMock 的 Mockito 是做到这一点的唯一方法吗?如果是,我们是否错误地使用了 Guice。

标签: java spring unit-testing guice


【解决方案1】:

好的,首先要做的事情是:您似乎没有按照预期的方式使用 Guice。一般来说,您希望使用Guice.createInjector() 来启动整个应用程序,并让它为您创建所有构造函数参数,而无需调用new

一个典型的用例可能是这样的:

public class Foo {
  private final TaskExecutor executor;

  @Inject
  public Foo(TaskExecutor executor) {
    this.executor = executor;
  }
}

这是可行的,因为 Foo 的实例是自己注入的,一直到对象图。见:Getting started

通过依赖注入,对象在其构造函数中接受依赖。要构造一个对象,首先要构建它的依赖关系。但是要构建每个依赖项,您需要它的依赖项,依此类推。所以当你构建一个对象时,你确实需要构建一个对象图。

手工构建对象图是劳动密集型的,容易出错,并且使测试变得困难。相反,Guice 可以为您构建对象图。但首先,Guice 需要配置为完全按照您的需要构建图形。

因此,通常情况下,您不会创建单例模式并将注入器放入其中,因为您应该很少在主类之外调用Guice.createInstance;让注射器为您完成所有工作。


话虽如此,要解决您实际询问的问题,您想使用Jukito

JUnit、Guice 和 Mockito 的综合力量。另外,这听起来像是一门很酷的武术。

让我们回到我上面描述的用例。在 Jukito 中,您可以这样写 FooTest

@RunWith(JukitoRunner.class)
public class FooTest {
  public static class Module extends JukitoModule {
    @Override
    protected void configureTest() {
      bindMock(TaskExecutor.class).in(TestSingleton.class);
    }
  }

  @Test
  public void testSomething(Foo foo, TaskExecutor executor) {
     foo.doSomething();
     verify(executor, times(2)).someMethod(eq("Hello World"));
  }
}

这将验证您的Mock object(由Mockito 通过Jukito 生成)已经使用字符串"Hello World" 两次调用someMethod 方法。

这就是为什么您不想以您描述的方式使用ObjectFactory 生成对象的原因; Jukito 在其单元测试中为您创建了 Injector,而要注入 Mock 将非常困难,并且您必须编写大量样板文件。

【讨论】:

  • 谢谢,这很有帮助。我们将首先回顾一下我们是如何使用 guice 的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 1970-01-01
  • 2022-10-18
相关资源
最近更新 更多