How do I substitute Guice modules with fake test modules for unit tests?

nik*_*hil 2 java spring unit-testing guice

Here's how we are using Guice in a new application:

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

Module1 defines how the TaskExecutor needs to be constructed.

In the code we use ObjectFactory.getInstance().getTaskExecutor() to obtain and the instance of TaskExecutor.

In unit tests we want to be able to replace this with a FakeTaskExecutor essentially we want to get an instance of FakeTaskExecutor when ObjectFactory.getInstance().getTaskExecutor() is called.

我正在考虑实现一个FakeModule将由注入器而不是Module1.

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

dur*_*597 5

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

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

public class Foo {
  private final TaskExecutor executor;

  @Inject
  public Foo(TaskExecutor executor) {
    this.executor = executor;
  }
}
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为 Foo 的实例本身是注入的,一直到对象图。请参阅:入门

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

手动构建对象图是劳动密集型的,容易出错,并且使测试变得困难。相反,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"));
  }
}
Run Code Online (Sandbox Code Playgroud)

这将验证您的Mock 对象,由Mockito通过 Jukito生成,someMethod两次都使用 String 对它调用了两次方法"Hello World"

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