package de.hilling.junit.cdi; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; import org.apache.deltaspike.core.api.provider.BeanProvider; import org.junit.runner.Description; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.mockito.Mock; import de.hilling.junit.cdi.annotations.ActivatableTestImplementation; import de.hilling.junit.cdi.lifecycle.LifecycleNotifier; import de.hilling.junit.cdi.scope.EventType; import de.hilling.junit.cdi.scope.InvocationTargetManager; import de.hilling.junit.cdi.util.LoggerConfigurator; import de.hilling.junit.cdi.util.ReflectionsUtils; /** * Runner for cdi tests providing: <ul> <li>test creation via cdi using deltaspike</li> <li>injection and activation of * mocks using MockManager</li> </ul> */ public class CdiUnitRunner extends BlockJUnit4ClassRunner { private static final Logger LOG = Logger.getLogger(CdiUnitRunner.class .getCanonicalName()); private final InvocationTargetManager invocationTargetManager; private final ContextControlWrapper contextControl = ContextControlWrapper.getInstance(); private static Map<Class<?>, Object> testCases = new HashMap<>(); private LifecycleNotifier lifecycleNotifier; static { LoggerConfigurator.configure(); } private CurrentTestInformation testInformation; public CdiUnitRunner(Class<?> klass) throws InitializationError { super(klass); invocationTargetManager = BeanProvider.getContextualReference(InvocationTargetManager.class, false); lifecycleNotifier = BeanProvider.getContextualReference(LifecycleNotifier.class, false); testInformation = resolveBean(CurrentTestInformation.class); } @Override protected Object createTest() { final Class<?> testClass = getTestClass().getJavaClass(); Object test = resolveTest(testClass); for (Field field : ReflectionsUtils.getAllFields(test.getClass())) { if (field.isAnnotationPresent(Mock.class)) { assignMockAndActivateProxy(field, test); } if (isTestActivatable(field)) { activateForTest(field); } } return test; } private boolean isTestActivatable(Field field) { Class type = field.getType(); return type.isAnnotationPresent(ActivatableTestImplementation.class); } private void activateForTest(Field field) { invocationTargetManager.activateAlternative(field.getType()); } private void assignMockAndActivateProxy(Field field, Object test) { field.setAccessible(true); try { Class<?> type = field.getType(); Object mock = invocationTargetManager.mock(type); field.set(test, mock); invocationTargetManager.activateMock(type); } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException(e); } finally { field.setAccessible(false); } } @Override protected void runChild(final FrameworkMethod method, RunNotifier notifier) { final Description description = describeChild(method); LOG.fine("> preparing " + description); invocationTargetManager.addAndActivateTest(description.getTestClass()); testInformation.setMethod(method.getMethod()); testInformation.setTestClass(description.getTestClass()); contextControl.startContexts(); lifecycleNotifier.notify(EventType.STARTING, description); LOG.fine(">> starting " + description); super.runChild(method, notifier); LOG.fine("<< finishing " + description); lifecycleNotifier.notify(EventType.FINISHING, description); contextControl.stopContexts(); lifecycleNotifier.notify(EventType.FINISHED, description); invocationTargetManager.reset(); LOG.fine("< finished " + description); } @SuppressWarnings("unchecked") protected <T> T resolveTest(Class<T> clazz) { if (!testCases.containsKey(clazz)) { testCases.put(clazz, resolveBean(clazz)); } return (T) testCases.get(clazz); } private <T> T resolveBean(Class<T> clazz) { return BeanProvider.getContextualReference(clazz, false); } }