Sunday, February 15, 2009

yo dawg.

I heard you like dependency injection so I put IOC in your tests so you can inject while you mock.

@RunWith(JDaveRunner.class)
public class MockitoSpec extends Specification<Void> {
@GuicedMock
private List<String> list;

@GuicedMock
@Named("hello")
private List<String> namedList;

private static class TargetClass {
@Inject
private List<String> list;

@Inject
@Named("hello")
private List<String> namedList;
}

private static class ConstructorInjection {
private final List<String> list;

@Inject
public ConstructorInjection(final List<String> list) {
this.list = list;
}
}

public class Injecting {
public void mocks() {
final Injector injector = Guice.createInjector(new Module() {
public void configure(final Binder binder) {
GuicedAnnotationEngine.binder.set(binder);
MockitoAnnotations.initMocks(MockitoSpec.this);
}
});
final TargetClass target = new TargetClass();
injector.injectMembers(target);
specify(target.list, is(list));
specify(target.namedList, is(namedList));
final ConstructorInjection constructorInjection = injector.getInstance(ConstructorInjection.class);
specify(constructorInjection.list, is(list));
}
}
}

and then...

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.Arrays.asList;

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.List;

import org.mockito.Mock;
import org.mockito.Mockito;

import com.google.inject.Binder;
import com.google.inject.BindingAnnotation;
import com.google.inject.Key;

public class GuicedAnnotationEngine implements AnnotationEngine {
public static ThreadLocal<Binder> binder = new ThreadLocal<Binder>();

public Object createMockFor(final Annotation annotation, final Field field) {
if (annotation instanceof Mock) {
return Mockito.mock(field.getType(), field.getName());
}
if (annotation instanceof GuicedMock) {
return bindMock(field.getType(), field);
}
return null;
}

private <T> T bindMock(final Class<T> type, final Field field) {
final T mock = Mockito.mock(type, field.getName());
final Annotation bindingAnnotation = getBindingAnnotation(field);
final Key<T> key = getKey(field.getGenericType(), bindingAnnotation);
binder.get().bind(key).toInstance(mock);
return mock;
}

private Annotation getBindingAnnotation(final Field field) {
final List<Annotation> annotations = asList(field.getAnnotations());
for (final Annotation annotation : annotations) {
final Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType.isAnnotationPresent(BindingAnnotation.class)) {
return annotation;
}
}
return null;
}

@SuppressWarnings("unchecked")
private <T> Key<T> getKey(final Type genericType, final Annotation bindingAnnotation) {
if (bindingAnnotation == null) {
return (Key<T>) Key.get(genericType);
}
return (Key<T>) Key.get(genericType, bindingAnnotation);
}

@Target(FIELD)
@Retention(RUNTIME)
public static @interface GuicedMock {
}
}

0 kommenttia: