如何用伪造的测试模块代替Guice模块进行单元测试? [英] How do I substitute Guice modules with fake test modules for unit tests?

查看:53
本文介绍了如何用伪造的测试模块代替Guice模块进行单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 公共类ObjectFactory {私有静态最终ObjectFactory实例= new ObjectFactory();私人最终喷油器喷油器;私有ObjectFactory()抛出RuntimeException {this.injector = Guice.createInjector(new Module1());}公共静态最终ObjectFactory getInstance(){返回实例;}public TaskExecutor getTaskExecutor(){返回jector.getInstance(TaskExecutor.class);}} 

Module1 定义了如何构造 TaskExecutor .

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

在单元测试中,我们希望能够将其替换为 FakeTaskExecutor ,实际上,我们希望在 ObjectFactory.getInstance()时获得 FakeTaskExecutor 的实例.getTaskExecutor()被调用.

我当时正在考虑实现一个 FakeModule ,供注入器使用,而不是 Module1 .

在Spring中,我们将只使用 @Autowired 批注,然后为 Test Production 代码定义单独的bean,并使用 Spring4JunitRunner ;我们正在尝试与Guice做类似的事情.

解决方案

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

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

 公共类Foo {私有的最终TaskExecutor执行器;@注入公共Foo(TaskExecutor执行程序){this.executor =执行者;}} 

之所以可行,是因为Foo的实例一直是自己注入的,一直到对象图的上方.请参阅:入门

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

手动构建对象图需要大量劳动,容易出错,并且使测试变得困难.相反,Guice可以为您构建对象图.但是首先,需要对Guice进行配置,使其完全根据您的需要构建图形.

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


话虽如此,要解决您真正要解决的问题,您想使用 Jukito .

JUnit,Guice和Mockito的组合功能.另外,这听起来像是一种很酷的武术.

让我们回到上面描述的用例.在Jukito中,您应这样编写 FooTest :

  @RunWith(JukitoRunner.class)公共类FooTest {公共静态类Module扩展了JukitoModule {@Override受保护的void configureTest(){bindMock(TaskExecutor.class).in(TestSingleton.class);}}@测试public void testSomething(Foo foo,TaskExecutor执行程序){foo.doSomething();verify(executor,times(2)).someMethod(eq("Hello World")));}} 

这将验证您模拟对象(由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);
  }
}

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.

I was thinking of implementing a FakeModule which would be used by the injector instead of the Module1.

In Spring, we would just use the @Autowired annotation and then define separate beans for Test and Production code and run our tests with the Spring4JunitRunner; we're trying to do something similar with Guice.

解决方案

Okay, first things first: You don't appear to be using Guice the way it is intended. Generally speaking, you want to use Guice.createInjector() to start up your entire application, and let it create all the constructor arguments for you without ever calling new.

A typical use case might be something like this:

public class Foo {
  private final TaskExecutor executor;

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

This works because the instances of Foo are themselves injected, all the way up the Object Graph. See: Getting started

With dependency injection, objects accept dependencies in their constructors. To construct an object, you first build its dependencies. But to build each dependency, you need its dependencies, and so on. So when you build an object, you really need to build an object graph.

Building object graphs by hand is labour intensive, error prone, and makes testing difficult. Instead, Guice can build the object graph for you. But first, Guice needs to be configured to build the graph exactly as you want it.

So, typically, you don't create a Singleton pattern and put the injector into it, because you should rarely call Guice.createInstance outside of your main class; let the injector do all the work for you.


All that being said, to solve the problem you're actually asking about, you want to use Jukito.

The combined power of JUnit, Guice and Mockito. Plus it sounds like a cool martial art.

Let's go back to the use case I've described above. In Jukito, you would write FooTest like this:

@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"));
  }
}

This will verify that your Mock object, generated by Mockito via Jukito has had the method someMethod called on it exactly two times with the String "Hello World" both times.

This is why you don't want to be generating objects with ObjectFactory in the way you describe; Jukito creates the Injector for you in its unit tests, and it would be very difficult to inject a Mock instead and you'd have to write a lot of boilerplate.

这篇关于如何用伪造的测试模块代替Guice模块进行单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆