JUnit tests made easy with Guice

Guice is an open source tool by Google that aims at simplifying the construction of software components especially when complex relationships exist between its parts.

Until I came across Guice (and Spring, to be honest), I was used to honor two main design patterns: factory methods and builder. The intent of these two patterns is to separate the construction of objects from their representation and from their usage. For instance, if you delegate the construction of instances of an interface to a factory method, which concrete class will be used to create the instances will depend on the particular implementation of the factory method which, in turn, will vary from application to application. On the other hand, using a builder you centralize the construction of complex objects so that the way these objects are built (and their dependencies in particular) is defined once for all in the code. Of course, if you follow these two patterns in Java you’ll have to abandon the new operator.

Guice is an easy way to blend these two patterns together into a single framework and much more. Thanks to the dependcy injection design pattern, Guice helps you decouple components and configure dependencies. All you have to do is define which interfaces are implemented by which concrete classes and which objects are to be injected in your components.

If you’re unit testing components with JUnit you surely face where to put the initialization code that reads the Guice configuration modules and creates the injector(s). As a trivial solution you could make every test class to extend a common class with a static method that is annotated with @BeforeClass; this method has to be used once per test to setup the Guice injector. While this is an acceptable solution, it doesn’t take full advantage of the Guice capabilities. When I faced this problem for the first time I asked myself what the best way to inject the components I needed in my tests was. I wanted to obtain something like this:

public class ServiceTest {
    private IService service;

    @Inject
    public void setService(IService service) {
        this.service = service;
    }

    @Test
    public void testApp() {
        Assert.assertEquals("Hello World!", service.doSomething());
    }
}

I then found a post about integrating Guice and JUnit that solved almost all my doubts. The author suggested to create a custom JUnit class runner that overrides the default behaviour and, in addition to instancing the test class, it configures Guice and injects the dependencies in the test class instance. The custom class runner must be passed to the JUnit @RunWith annotation. This is a more elegant solution with respect to the before mentioned trivial one. But still it had something I didn’t like: the fact that the Guice modules have to be passed to the constructor of the custom class runner. Wouldn’t it be better if the list of Guice modules was declared within the unit test as a parameter of an annotation?

I got inspired by how JUnit test suite works and I wrote my own class runner:

public class GuiceJUnitRunner extends BlockJUnit4ClassRunner {
    private Injector injector;

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    public @interface GuiceModules {
        Class<?>[] value();
    }

    @Override
    public Object createTest() throws Exception {
        Object obj = super.createTest();
        injector.injectMembers(obj);
        return obj;
    }

    public GuiceJUnitRunner(Class<?> klass) throws InitializationError {
        super(klass);
        Class<?>[] classes = getModulesFor(klass);
        injector = createInjectorFor(classes);
    }

    private Injector createInjectorFor(Class<?>[] classes) throws InitializationError {
        Module[] modules = new Module[classes.length];
        for (int i = 0; i < classes.length; i++) {
            try {
                modules[i] = (Module) (classes[i]).newInstance();
            } catch (InstantiationException e) {
                throw new InitializationError(e);
            } catch (IllegalAccessException e) {
                throw new InitializationError(e);
            }
        }
        return Guice.createInjector(modules);
    }

    private Class<?>[] getModulesFor(Class<?> klass) throws InitializationError {
        GuiceModules annotation = klass.getAnnotation(GuiceModules.class);
        if (annotation == null)
            throw new InitializationError(
                    "Missing @GuiceModules annotation for unit test '" + klass.getName()
                            + "'");
        return annotation.value();
    }
}

Basically, if you use this runner your test will be :

@RunWith(GuiceJUnitRunner.class)
@GuiceModules({ ComponentsTestModule.class, ServicesTestModule.class })
public class ServiceTest {
    private IService service;

    @Inject
    public void setService(IService service) {
        this.service = service;
    }

    @Test
    public void testApp() {
        Assert.assertEquals("Hello World!", service.doSomething());
    }
}

I think this is a cleaner approach and, in fact, it is very similar to how unit tests are configured with Spring. The advantage is that every needed module is declared clearly in the unit test and could be easily added or removed.
I uploaded the code on GitHub, feel free to check it out from my account and leave a feedback: GuiceJUnitRunner.

Project is based on Maven2 and Eclipse. These are some easy steps to download and test it:

% git clone git://github.com/sfragis/GuiceJUnitRunner.git
% cd GuiceJUnitRunner
GuiceJUnitRunner% mvn test
Leave a comment ?

11 Comments.

  1. This is exactly what I’ve been looking for, thanks!

  2. Awesome! Thank you!

  3. Your impl provide one instance of Injector per test class… Is there a way to avoid that, using one single instance for all test classes?

    • Fabio Strozzi

      Hi Carlos, thanks for posting.
      I didn’t try but you might slightly change the GuiceJUniteRunner to instance the injector only once.
      For instance:

      public class GuiceJUnitRunner extends BlockJUnit4ClassRunner {
          private static Injector injector;
      
          private Injector createInjectorFor(Class<?>[] classes) throws InitializationError {
              if (injector != null)
                  return injector;
              // rest of the method as-is
          }
      
          // rest of the code as-is
      

      Also, you might annotate the test suite class that you use to start the tests with both the @RunWith and the @GuiceModules annotations.
      This if the quickest and dirtiest solution that come to my mind but it should work.
      Let me know if you improve it or devise something better.
      Fabio

  4. Great work, Fabio! Simply elegant! I just love it! 🙂

  5. very nice work.

  6. Very nice, thanks! Downloaded and using 🙂

  7. This was very helpful to me. Thanks!

  8. DRY and KISS, cool approach! Have you ever thought about releasing it? Or maybe I’m not aware and similar solution has already been released? Let me know!

  9. Thank You very much for this nice solution. I like the idea of passing a list of modules to the GuiceJUnitRunner. Great!

  10. Really awesome!

    You wrote this 5 years ago but this is still working and efficient.
    Thank you !

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>