34

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

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

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.

Pang
  • 9,564
  • 146
  • 81
  • 122
yeraycaballero
  • 1,603
  • 3
  • 19
  • 29

4 Answers4

48

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.

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:

/** 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.

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."

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.

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.

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).

Mason Wan
  • 118
  • 1
  • 8
NamshubWriter
  • 23,549
  • 2
  • 41
  • 59
  • Thanks so much. Your arguments are very clear. I've dependencies that aren't created after application was deployed – yeraycaballero Apr 22 '10 at 09:55
  • 2
    `my tests often inject null into the constructor to keep the tests simple` => that may be an indication that your class may not be as cohesive – beluchin Aug 06 '14 at 22:24
  • @beluchin could you explain what you mean? I try to avoid doing "real work" in my constructor, so passing in a `null` into the constructor is rarely a problem. If I am testing a class, and the method(s) I am testing doesn't use one of the fields, passing a `null` for that constructor parameter the simplest thing to do. If that doesn't work, I inject a mock object (for services) or a real instance (for value objects) but either of these makes the code more complicated than it would be if I just passed in `null` – NamshubWriter Sep 21 '14 at 16:55
  • 1
    @NamshubWriter one way to define **strong** cohesion is that **all** public methods eventually reference **all** the class level variables. Thus, you can get away with passing a `null` dependency to the constructor in your test because the public methods you are calling in your test do not reference the dependency - lacking cohesion, by definition. – beluchin Sep 22 '14 at 02:36
  • @beluchin I think insisting that all public methods eventually reference all fields is a bit extreme. I also don't think that's really relevant to this particular topic. – NamshubWriter Sep 22 '14 at 16:26
  • Thanks! Just a note - where you say: "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.". Just to be clear, the above unit test doesn't test that B can be or indeed is created does it? – Mark D Jul 27 '15 at 06:57
  • 1
    @MarkD In my solution, `B` is `UserDAO` and the test does not verify that it can be created. The module guarantees that if you pass `LoginServiceModule` to `Guice.create injector()` and Guice doesn't have the bindings necessary to create `UserDao`, `createInjector` will throw an exception – NamshubWriter Jul 27 '15 at 07:02
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.

murungu
  • 2,090
  • 4
  • 21
  • 45
2

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).

gpampara
  • 11,989
  • 3
  • 27
  • 26
  • 2
    I think you might need to understand DI a little more. The point is that the DI framework "will" result in built up instances and will inject them for you where appropriate (that's why they were created) - you will be separating your creation and business logic. Now you can test them separately. Testing your creation code, however, does not mean trying to make sure that Guice works as intended. If you don't trust the library, perhaps you should use something else. – gpampara Mar 16 '10 at 12:02
  • 12
    The Guice guys do not agree with your statement. Citation: "If your providers are complex, be sure to test them!" (http://code.google.com/p/google-guice/wiki/ProviderBindings) – Joe23 May 04 '11 at 14:10
  • 5
    @Joe23 - testing Guice providers and testing Guice bindings are 2 different things. – topchef Jun 15 '11 at 19:32
  • 1
    I disagree -- errors from Guice configuration will materialize at run-time and threaten to make your software completely unavailable, not just operational with defects. You definitely want to make sure it is working as you expect it to, even though writing tests for these cases takes a little extra planning and consideration. I'd much rather write bad logic that ends up failing an injection test, and know about it on my laptop, rather than pushing something sketchy through CI and waste my teammate's time -- or depending on the maturity of the deployment tooling, much worse. – Jameson Mar 17 '17 at 02:02
0

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.

Alexander Malfait
  • 2,691
  • 1
  • 23
  • 23