我如何测试Guice注射?

时间:2022-09-25 10:54:16

I gave to Google Guice the responsibility of wiring my objects. But, how can I test if the bindings are working well?

我向Google Guice提供了连接我的物品的责任。但是,我如何测试绑定是否运行良好?

For example, suppose we have a class A which has a dependence B. How can I test that B is injected correctly?

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

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

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

Notice that A hasn't got a getB() method and I want to assert that A.b isn't null.

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

4 个解决方案

#1


41  

For any complex Guice project, you should add tests to make sure that the modules can be used to create your classes. In your example, if B were a type that Guice couldn't figure out how to create, then Guice won't be able to create A. If A wasn't needed to start the server but was needed when your server was handling a request, that would cause problems.

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

In my projects, I write tests for non-trivial modules. For each module, I use requireBinding() to declare what bindings the module requires but doesn't define. In my tests, I create a Guice injector using the module under test and another module that provides the required bindings. Here's an example using JUnit4 and JMock:

在我的项目中,我为非平凡模块编写测试。对于每个模块,我使用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;
    }
  }
}

Notice how the test only asks for a provider. That's sufficient to determine that Guice could resolve the bindings. If LoginService was created by a provider method, this test wouldn't test the code in the provider method.

请注意该测试仅询问提供者。这足以确定Guice可以解决绑定问题。如果LoginService是由提供程序方法创建的,则此测试不会测试提供程序方法中的代码。

This test also doesn't test that you binded the right thing to UserDao, or that UserDao was scoped correctly. Some would argue that those types of things are rarely worth checking; if there's a problem, it happens once. You should "test until fear turns to boredom."

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

I find Module tests useful because I often add new injection points, and it's easy to forget to add a binding.

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

The requireBinding() calls can help Guice catch missing bindings before it returns your injector! In the above example, the test would still work if the requireBinding() calls were not there, but I like having them because they serve as documentation.

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

For more complicated modules (like my root module) I might use Modules.override() to override bindings that I don't want at test time (for instance, if I want to verify that my root object to be created, I probably don't want it to create an object that will connect to the database). For simple projects, you might only test the top-level module.

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

Note that Guice will not inject nulls unless the field as annotated with @Nullable so you very rarely need to verify that the injected objects are non-null in your tests. In fact, when I annotate constructors with @Inject I do not bother to check if the parameters are null (in fact, my tests often inject null into the constructor to keep the tests simple).

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

#2


4  

Another way to test your configuration is by having a test suite that tests your app end-to-end. Although end-to-end tests nominally test use cases they indirectly check that your app is configured correctly, (that all the dependencies are wired, etc etc). Unit tests on the other hand should focus exclusively on the domain, and not on the context in which your code is deployed.

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

I also agree with NamshubWriter's answer. I'm am not against tests that check configuration as long as they are grouped in a separate test suite to your unit tests.

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

#3


3  

IMHO, you should not be testing that. The Google Guice guys have the unit tests to assert that the injections work as expected - after all, that's what Guice is designed to do. You should only be writing tests for your own code (A and B).

恕我直言,你不应该测试。谷歌Guice家伙有单元测试断言注射按预期工作 - 毕竟,这就是Guice的目的。您应该只为自己的代码(A和B)编写测试。

#4


-1  

I don't think you should test private members being set. Better to test against the public interface of your class. If member "b" wouldn't be injected, you'll probably get a NullPointerException executing your tests, which should be plenty of warning.

我认为你不应该测试私人成员的设置。最好测试你班级的公共界面。如果不注入成员“b”,你可能会得到一个执行测试的NullPointerException,这应该是很多警告。

#1


41  

For any complex Guice project, you should add tests to make sure that the modules can be used to create your classes. In your example, if B were a type that Guice couldn't figure out how to create, then Guice won't be able to create A. If A wasn't needed to start the server but was needed when your server was handling a request, that would cause problems.

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

In my projects, I write tests for non-trivial modules. For each module, I use requireBinding() to declare what bindings the module requires but doesn't define. In my tests, I create a Guice injector using the module under test and another module that provides the required bindings. Here's an example using JUnit4 and JMock:

在我的项目中,我为非平凡模块编写测试。对于每个模块,我使用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;
    }
  }
}

Notice how the test only asks for a provider. That's sufficient to determine that Guice could resolve the bindings. If LoginService was created by a provider method, this test wouldn't test the code in the provider method.

请注意该测试仅询问提供者。这足以确定Guice可以解决绑定问题。如果LoginService是由提供程序方法创建的,则此测试不会测试提供程序方法中的代码。

This test also doesn't test that you binded the right thing to UserDao, or that UserDao was scoped correctly. Some would argue that those types of things are rarely worth checking; if there's a problem, it happens once. You should "test until fear turns to boredom."

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

I find Module tests useful because I often add new injection points, and it's easy to forget to add a binding.

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

The requireBinding() calls can help Guice catch missing bindings before it returns your injector! In the above example, the test would still work if the requireBinding() calls were not there, but I like having them because they serve as documentation.

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

For more complicated modules (like my root module) I might use Modules.override() to override bindings that I don't want at test time (for instance, if I want to verify that my root object to be created, I probably don't want it to create an object that will connect to the database). For simple projects, you might only test the top-level module.

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

Note that Guice will not inject nulls unless the field as annotated with @Nullable so you very rarely need to verify that the injected objects are non-null in your tests. In fact, when I annotate constructors with @Inject I do not bother to check if the parameters are null (in fact, my tests often inject null into the constructor to keep the tests simple).

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

#2


4  

Another way to test your configuration is by having a test suite that tests your app end-to-end. Although end-to-end tests nominally test use cases they indirectly check that your app is configured correctly, (that all the dependencies are wired, etc etc). Unit tests on the other hand should focus exclusively on the domain, and not on the context in which your code is deployed.

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

I also agree with NamshubWriter's answer. I'm am not against tests that check configuration as long as they are grouped in a separate test suite to your unit tests.

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

#3


3  

IMHO, you should not be testing that. The Google Guice guys have the unit tests to assert that the injections work as expected - after all, that's what Guice is designed to do. You should only be writing tests for your own code (A and B).

恕我直言,你不应该测试。谷歌Guice家伙有单元测试断言注射按预期工作 - 毕竟,这就是Guice的目的。您应该只为自己的代码(A和B)编写测试。

#4


-1  

I don't think you should test private members being set. Better to test against the public interface of your class. If member "b" wouldn't be injected, you'll probably get a NullPointerException executing your tests, which should be plenty of warning.

我认为你不应该测试私人成员的设置。最好测试你班级的公共界面。如果不注入成员“b”,你可能会得到一个执行测试的NullPointerException,这应该是很多警告。