package org.ovirt.engine.arquillian.mockito; import static org.mockito.Mockito.spy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.HashSet; import java.util.Set; import javax.enterprise.event.Observes; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.InjectionPoint; import javax.enterprise.inject.spi.InjectionTarget; import javax.enterprise.inject.spi.ProcessBean; import javax.enterprise.inject.spi.ProcessInjectionTarget; import javax.enterprise.inject.spi.ProcessProducer; import javax.enterprise.inject.spi.Producer; import org.mockito.Spy; /** * Allow the usage of Mockito's @Spy annotation within Arquillian managed tests. * <p> * This class hooks itself into the bean creation process and wraps a spy around injected resources. * To make use of this, the injection point in the test class must be annotated with @Inject and @Spy. * <p> * The class is heavily inspired by https://github.com/topikachu/arquillian-extension-mockito */ public class SpyAnnotation implements Extension { private Set<Class> spiedClasses = new HashSet<>(); public <X> void processBean(@Observes ProcessBean<X> event) { for (InjectionPoint injectionPoint : event.getBean().getInjectionPoints()) { Spy spy = injectionPoint.getAnnotated().getAnnotation(Spy.class); if (spy != null) { final Type spiedType = injectionPoint.getAnnotated().getBaseType(); if (spiedType instanceof Class) { spiedClasses.add((Class) spiedType); } else if (spiedType instanceof ParameterizedType) { spiedClasses.add((Class) ((ParameterizedType) spiedType).getActualTypeArguments()[0]); } } } } @SuppressWarnings("unchecked") public <T> void processInjectTarget(@Observes final ProcessInjectionTarget<T> event) { event.setInjectionTarget((InjectionTarget<T>) Proxy.newProxyInstance( SpyAnnotation.class.getClassLoader(), new Class[] { InjectionTarget.class }, new ProducerInvocationHandler(event.getInjectionTarget()))); } @SuppressWarnings("unchecked") public <T, X> void processProducer(@Observes final ProcessProducer<T, X> event) { event.setProducer((Producer<X>) Proxy.newProxyInstance( SpyAnnotation.class.getClassLoader(), new Class[] { Producer.class }, new ProducerInvocationHandler(event.getProducer()))); } class ProducerInvocationHandler implements InvocationHandler { private Producer<?> producer; public ProducerInvocationHandler(Producer<?> producer) { this.producer = producer; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object object = method.invoke(producer, args); if (object != null && method.getName().equals("produce") && spiedClasses.contains(object.getClass())) { object = spy(object); } return object; } } }