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
Follow Me!