package de.dhbw.humbuch.guice;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.junit.runner.Runner;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
/**
* JUnit {@link Runner} using Guice to inject test classes annotated with
* {@link GuiceModules}
*/
public class GuiceJUnitRunner extends BlockJUnit4ClassRunner {
private Injector injector;
/**
* Specifies the Guice {@link Module} classes which should be used when
* injecting a JUnit test class
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface GuiceModules {
public Class<? extends Module>[] value();
}
/**
* Initialise {@link Runner}, configure Guice using the {@link Module}s
* configured in the {@link GuiceModules} annotation, inject members into
* test class
*
* @param testClass
* JUnit test class
* @throws InitializationError
* thrown when an error occurs during the initialisation of
* Guice modules or injection of members
*/
public GuiceJUnitRunner(Class<?> testClass) throws InitializationError {
super(testClass);
Set<Class<? extends Module>> moduleClasses = getModuleClassesFor(testClass);
injector = createInjectorFor(moduleClasses);
}
@Override
public Object createTest() throws Exception {
Object object = super.createTest();
injector.injectMembers(object);
return object;
}
/**
* Creates a Guice {@link Injector} with a given {@link Set} of
* {@link Module} classes
*
* @param moduleClasses
* {@link Set} of {@link Module} classes
* @return a Guice {@link Injector} with the specified modules
* @throws InitializationError
* thrown when an error occurs instantiating the {@link Module}
* class
*/
private Injector createInjectorFor(
Set<Class<? extends Module>> moduleClasses)
throws InitializationError {
Set<Module> modules = new HashSet<>();
for (Class<?> moduleClass : moduleClasses) {
try {
modules.add((Module) moduleClass.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
throw new InitializationError(e);
}
}
return Guice.createInjector(modules);
}
/**
* Reads the {@link Module} classes configured in a {@link GuiceModules}
* annotation of the test class
*
* @param testClass
* class with the {@link GuiceModules} annotation
* @return a {@link Set} of Guice {@link Module} classes
* @throws InitializationError
* thrown when no {@link GuiceModules} annotation is present
*/
private Set<Class<? extends Module>> getModuleClassesFor(Class<?> testClass)
throws InitializationError {
GuiceModules annotation = testClass.getAnnotation(GuiceModules.class);
if (annotation == null)
throw new InitializationError(
"Missing @GuiceModules annotation for unit test '"
+ testClass.getName() + "'");
return new HashSet<>(Arrays.asList(annotation.value()));
}
}