From a1c62e7c047cbd8d0a7651b93feef79c0c1b6f79 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sat, 18 Jun 2016 13:19:07 +0200 Subject: [PATCH] Create delayed injection test runner - Test runner supporting new "DelayedInjection" annotation: such fields are only initialized with instantiation right before the first time they're used in tests, allowing to set up mock behavior beforehand --- .../AuthMeServiceInitializer.java | 42 +---- .../initialization/ConstructorInjection.java | 2 +- .../authme/initialization/FieldInjection.java | 2 +- .../initialization/InjectionHelper.java | 66 ++++++++ .../initialization/InstantiationFallback.java | 2 +- .../java/fr/xephi/authme/DelayedInject.java | 16 ++ .../xephi/authme/DelayedInjectionRunner.java | 154 ++++++++++++++++++ .../fr/xephi/authme/ReflectionTestUtils.java | 31 +++- .../authme/command/CommandMapperTest.java | 17 +- .../authme/listener/ListenerServiceTest.java | 23 +-- .../xephi/authme/task/PurgeServiceTest.java | 49 +----- .../tools/checktestmocks/CheckTestMocks.java | 9 +- .../tools/dependencygraph/DrawDependency.java | 9 +- 13 files changed, 296 insertions(+), 126 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/initialization/InjectionHelper.java create mode 100644 src/test/java/fr/xephi/authme/DelayedInject.java create mode 100644 src/test/java/fr/xephi/authme/DelayedInjectionRunner.java diff --git a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java index 29e5adcf..33df29d0 100644 --- a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java +++ b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java @@ -5,7 +5,6 @@ import com.google.common.collect.ImmutableSet; import fr.xephi.authme.settings.NewSetting; import javax.annotation.PostConstruct; -import javax.inject.Provider; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -167,8 +166,7 @@ public class AuthMeServiceInitializer { * @return the instantiated object */ private T instantiate(Class clazz, Set> traversedClasses) { - Injection injection = firstNotNull( - ConstructorInjection.provide(clazz), FieldInjection.provide(clazz), InstantiationFallback.provide(clazz)); + Injection injection = InjectionHelper.getInjection(clazz); if (injection == null) { throw new IllegalStateException("Did not find injection method for " + clazz + ". Make sure you have " + "a constructor with @Inject or fields with @Inject. Fields with @Inject require " @@ -268,7 +266,7 @@ public class AuthMeServiceInitializer { * @param object the object to execute the post construct method for */ private static void executePostConstructMethod(Object object) { - Method postConstructMethod = getAndValidatePostConstructMethod(object.getClass()); + Method postConstructMethod = InjectionHelper.getAndValidatePostConstructMethod(object.getClass()); if (postConstructMethod != null) { try { postConstructMethod.setAccessible(true); @@ -285,40 +283,4 @@ public class AuthMeServiceInitializer { } } - /** - * Validate and locate the given class' post construct method. Returns {@code null} if none present. - * - * @param clazz the class to search - * @return post construct method, or null - */ - private static Method getAndValidatePostConstructMethod(Class clazz) { - Method postConstructMethod = null; - for (Method method : clazz.getDeclaredMethods()) { - if (method.isAnnotationPresent(PostConstruct.class)) { - if (postConstructMethod != null) { - throw new IllegalStateException("Multiple methods with @PostConstruct on " + clazz); - } else if (method.getParameterTypes().length > 0 || Modifier.isStatic(method.getModifiers())) { - throw new IllegalStateException("@PostConstruct method may not be static or have any parameters. " - + "Invalid method in " + clazz); - } else if (method.getReturnType() != void.class) { - throw new IllegalStateException("@PostConstruct method must have return type void. " - + "Offending class: " + clazz); - } else { - postConstructMethod = method; - } - } - } - return postConstructMethod; - } - - @SafeVarargs - private static Injection firstNotNull(Provider>... providers) { - for (Provider> provider : providers) { - Injection object = provider.get(); - if (object != null) { - return object; - } - } - return null; - } } diff --git a/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java b/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java index 74ea28b2..e80ea128 100644 --- a/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java +++ b/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java @@ -11,7 +11,7 @@ import java.lang.reflect.InvocationTargetException; /** * Functionality for constructor injection. */ -public class ConstructorInjection implements Injection { +class ConstructorInjection implements Injection { private final Constructor constructor; diff --git a/src/main/java/fr/xephi/authme/initialization/FieldInjection.java b/src/main/java/fr/xephi/authme/initialization/FieldInjection.java index c74e7c27..096c5f52 100644 --- a/src/main/java/fr/xephi/authme/initialization/FieldInjection.java +++ b/src/main/java/fr/xephi/authme/initialization/FieldInjection.java @@ -16,7 +16,7 @@ import java.util.List; /** * Functionality for field injection. */ -public class FieldInjection implements Injection { +class FieldInjection implements Injection { private final Field[] fields; private final Constructor defaultConstructor; diff --git a/src/main/java/fr/xephi/authme/initialization/InjectionHelper.java b/src/main/java/fr/xephi/authme/initialization/InjectionHelper.java new file mode 100644 index 00000000..04e186b7 --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/InjectionHelper.java @@ -0,0 +1,66 @@ +package fr.xephi.authme.initialization; + +import javax.annotation.PostConstruct; +import javax.inject.Provider; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Helper class for functions relating to injecting. + */ +public class InjectionHelper { + + private InjectionHelper() { + } + + /** + * Returns the {@link Injection} for the given class, or null if none applicable. + * + * @param clazz the class to process + * @param the class' type + * @return injection of the class or null if none detected + */ + public static Injection getInjection(Class clazz) { + return firstNotNull( + ConstructorInjection.provide(clazz), + FieldInjection.provide(clazz), + InstantiationFallback.provide(clazz)); + } + + /** + * Validates and locates the given class' post construct method. Returns {@code null} if none present. + * + * @param clazz the class to search + * @return post construct method, or null + */ + public static Method getAndValidatePostConstructMethod(Class clazz) { + Method postConstructMethod = null; + for (Method method : clazz.getDeclaredMethods()) { + if (method.isAnnotationPresent(PostConstruct.class)) { + if (postConstructMethod != null) { + throw new IllegalStateException("Multiple methods with @PostConstruct on " + clazz); + } else if (method.getParameterTypes().length > 0 || Modifier.isStatic(method.getModifiers())) { + throw new IllegalStateException("@PostConstruct method may not be static or have any parameters. " + + "Invalid method in " + clazz); + } else if (method.getReturnType() != void.class) { + throw new IllegalStateException("@PostConstruct method must have return type void. " + + "Offending class: " + clazz); + } else { + postConstructMethod = method; + } + } + } + return postConstructMethod; + } + + @SafeVarargs + private static Injection firstNotNull(Provider>... providers) { + for (Provider> provider : providers) { + Injection object = provider.get(); + if (object != null) { + return object; + } + } + return null; + } +} diff --git a/src/main/java/fr/xephi/authme/initialization/InstantiationFallback.java b/src/main/java/fr/xephi/authme/initialization/InstantiationFallback.java index 7a1c0849..4861733f 100644 --- a/src/main/java/fr/xephi/authme/initialization/InstantiationFallback.java +++ b/src/main/java/fr/xephi/authme/initialization/InstantiationFallback.java @@ -11,7 +11,7 @@ import java.lang.reflect.InvocationTargetException; * Fallback instantiation method for classes with an accessible no-args constructor * and no elements whatsoever annotated with {@link Inject} or {@link PostConstruct}. */ -public class InstantiationFallback implements Injection { +class InstantiationFallback implements Injection { private final Constructor constructor; diff --git a/src/test/java/fr/xephi/authme/DelayedInject.java b/src/test/java/fr/xephi/authme/DelayedInject.java new file mode 100644 index 00000000..eccd7b7c --- /dev/null +++ b/src/test/java/fr/xephi/authme/DelayedInject.java @@ -0,0 +1,16 @@ +package fr.xephi.authme; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks fields to be instantiated right before a method is invoked on them for the first time. + * + * @see DelayedInjectionRunner + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface DelayedInject { +} diff --git a/src/test/java/fr/xephi/authme/DelayedInjectionRunner.java b/src/test/java/fr/xephi/authme/DelayedInjectionRunner.java new file mode 100644 index 00000000..8eb66b8d --- /dev/null +++ b/src/test/java/fr/xephi/authme/DelayedInjectionRunner.java @@ -0,0 +1,154 @@ +package fr.xephi.authme; + +import fr.xephi.authme.initialization.Injection; +import fr.xephi.authme.initialization.InjectionHelper; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkField; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.internal.runners.util.FrameworkUsageValidator; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Custom JUnit runner which adds support for {@link DelayedInject}, along with Mockito's + * {@link Mock}, {@link org.mockito.Spy} and {@link org.mockito.InjectMocks}. + *

+ * Unlike Mockito's @InjectMocks, fields annotated with {@link DelayedInject} will be + * instantiated only right before a method is invoked on them. This allows a developer to + * define the behavior of mocks the test class depends on. With {@link org.mockito.InjectMocks}, + * fields are instantiated even before {@link org.junit.Before} methods, making it impossible + * to define behavior before the class is instantiated. + *

+ * Note that it is required to declare all dependencies of classes annotated with + * {@link DelayedInject} as {@link Mock} fields. If a dependency is missing, an exception + * will be thrown. + *

+ * Additionally, this runner adds support for {@link javax.annotation.PostConstruct} methods, + * both for Mockito's @InjectMocks and the custom @DelayedInject. + */ +public class DelayedInjectionRunner extends BlockJUnit4ClassRunner { + + public DelayedInjectionRunner(Class clazz) throws InitializationError { + super(clazz); + } + + @Override + public Statement withBefores(FrameworkMethod method, Object target, Statement statement) { + // Initialize all mocks + MockitoAnnotations.initMocks(target); + + // Add support for @DelayedInject and @PostConstruct + runPostConstructOnInjectMocksFields(target); + initializeDelayedMocks(target); + + // Send to parent + return super.withBefores(method, target, statement); + } + + @Override + public void run(final RunNotifier notifier) { + // add listener that validates framework usage at the end of each test + notifier.addListener(new FrameworkUsageValidator(notifier)); + super.run(notifier); + } + + private void runPostConstructOnInjectMocksFields(Object target) { + List delayedFields = getTestClass().getAnnotatedFields(InjectMocks.class); + for (FrameworkField field : delayedFields) { + Object o = ReflectionTestUtils.getFieldValue(field.getField(), target); + executePostConstructMethod(o); + } + } + + private void initializeDelayedMocks(Object target) { + List delayedFields = getTestClass().getAnnotatedFields(DelayedInject.class); + for (FrameworkField field : delayedFields) { + setUpField(target, field.getField()); + } + } + + private void setUpField(Object target, Field field) { + final Injection injection = InjectionHelper.getInjection(field.getType()); + if (injection == null) { + throw new IllegalStateException("No injection method available for field '" + field.getName() + "'"); + } + final Object[] dependencies = fulfillDependencies(target, injection.getDependencies()); + + Object delayedInjectionMock = Mockito.mock(field.getType(), + new DelayedInstantiatingAnswer(injection, dependencies)); + ReflectionTestUtils.setField(field, target, delayedInjectionMock); + } + + private Object[] fulfillDependencies(Object target, Class[] dependencies) { + List availableMocks = getTestClass().getAnnotatedFields(Mock.class); + Map, Object> mocksByType = new HashMap<>(); + for (FrameworkField frameworkField : availableMocks) { + Field field = frameworkField.getField(); + Object fieldValue = ReflectionTestUtils.getFieldValue(field, target); + mocksByType.put(field.getType(), fieldValue); + } + + Object[] resolvedValues = new Object[dependencies.length]; + for (int i = 0; i < dependencies.length; ++i) { + Object o = mocksByType.get(dependencies[i]); + if (o == null) { + throw new IllegalStateException("No mock found for '" + dependencies[i] + "'. " + + "All dependencies of @DelayedInject must be provided as @Mock fields"); + } + resolvedValues[i] = o; + } + return resolvedValues; + } + + /** + * Executes the class' PostConstruct method if available. Validates that all rules for + * {@link javax.annotation.PostConstruct} are met. + * + * @param object the object whose PostConstruct method should be run, if available + * @see InjectionHelper#getAndValidatePostConstructMethod + */ + private static void executePostConstructMethod(Object object) { + Method postConstructMethod = InjectionHelper.getAndValidatePostConstructMethod(object.getClass()); + if (postConstructMethod != null) { + ReflectionTestUtils.invokeMethod(postConstructMethod, object); + } + } + + private static final class DelayedInstantiatingAnswer implements Answer { + + private final Injection injection; + private final Object[] dependencies; + private Object realObject; + + public DelayedInstantiatingAnswer(Injection injection, Object... dependencies) { + this.injection = injection; + this.dependencies = dependencies; + } + + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + if (realObject == null) { + Object realObject = injection.instantiateWith(dependencies); + executePostConstructMethod(realObject); + this.realObject = realObject; + } + + Method method = invocation.getMethod(); + return ReflectionTestUtils.invokeMethod(method, realObject, invocation.getArguments()); + } + } + +} diff --git a/src/test/java/fr/xephi/authme/ReflectionTestUtils.java b/src/test/java/fr/xephi/authme/ReflectionTestUtils.java index 397181bb..887f443b 100644 --- a/src/test/java/fr/xephi/authme/ReflectionTestUtils.java +++ b/src/test/java/fr/xephi/authme/ReflectionTestUtils.java @@ -1,6 +1,7 @@ package fr.xephi.authme; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import static java.lang.String.format; @@ -33,6 +34,15 @@ public final class ReflectionTestUtils { } } + public static void setField(Field field, Object instance, Object value) { + try { + field.setAccessible(true); + field.set(instance, value); + } catch (IllegalAccessException e) { + throw new UnsupportedOperationException(e); + } + } + private static Field getField(Class clazz, T instance, String fieldName) { try { Field field = clazz.getDeclaredField(fieldName); @@ -44,13 +54,23 @@ public final class ReflectionTestUtils { } } + public static Object getFieldValue(Field field, Object instance) { + try { + field.setAccessible(true); + return field.get(instance); + } catch (IllegalAccessException e) { + throw new UnsupportedOperationException("Cannot get value of field '" + + field + "' for '" + instance + "'", e); + } + } + public static Object getFieldValue(Class clazz, T instance, String fieldName) { Field field = getField(clazz, instance, fieldName); try { return field.get(instance); } catch (IllegalAccessException e) { - throw new UnsupportedOperationException("Could not get value of field '" + fieldName + "'"); + throw new UnsupportedOperationException("Could not get value of field '" + fieldName + "'", e); } } @@ -73,4 +93,13 @@ public final class ReflectionTestUtils { + clazz.getName() + "'"); } } + + public static Object invokeMethod(Method method, Object instance, Object... parameters) { + method.setAccessible(true); + try { + return method.invoke(instance, parameters); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new UnsupportedOperationException("Could not invoke method '" + method + "'", e); + } + } } diff --git a/src/test/java/fr/xephi/authme/command/CommandMapperTest.java b/src/test/java/fr/xephi/authme/command/CommandMapperTest.java index 28ee861f..4bf022bd 100644 --- a/src/test/java/fr/xephi/authme/command/CommandMapperTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandMapperTest.java @@ -1,5 +1,7 @@ package fr.xephi.authme.command; +import fr.xephi.authme.DelayedInject; +import fr.xephi.authme.DelayedInjectionRunner; import fr.xephi.authme.command.TestCommandsUtil.TestLoginCommand; import fr.xephi.authme.command.TestCommandsUtil.TestRegisterCommand; import fr.xephi.authme.command.TestCommandsUtil.TestUnregisterCommand; @@ -10,6 +12,8 @@ import org.bukkit.command.CommandSender; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; import java.util.List; import java.util.Set; @@ -32,12 +36,20 @@ import static org.mockito.Mockito.mock; /** * Test for {@link CommandMapper}. */ +@RunWith(DelayedInjectionRunner.class) public class CommandMapperTest { private static Set commands; + + @DelayedInject private CommandMapper mapper; + + @Mock private PermissionsManager permissionsManager; + @Mock + private CommandInitializer commandInitializer; + @BeforeClass public static void setUpCommandHandler() { commands = TestCommandsUtil.generateCommands(); @@ -45,10 +57,7 @@ public class CommandMapperTest { @Before public void setUpMocks() { - permissionsManager = mock(PermissionsManager.class); - CommandInitializer initializer = mock(CommandInitializer.class); - given(initializer.getCommands()).willReturn(commands); - mapper = new CommandMapper(initializer, permissionsManager); + given(commandInitializer.getCommands()).willReturn(commands); } // ----------- diff --git a/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java b/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java index 6b87ffe1..5ace33c4 100644 --- a/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java +++ b/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java @@ -1,5 +1,7 @@ package fr.xephi.authme.listener; +import fr.xephi.authme.DelayedInject; +import fr.xephi.authme.DelayedInjectionRunner; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.hooks.PluginHooks; @@ -15,11 +17,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; -import java.lang.reflect.Method; import java.util.Arrays; import static org.hamcrest.Matchers.equalTo; @@ -32,9 +30,10 @@ import static org.mockito.Mockito.verifyZeroInteractions; /** * Test for {@link ListenerService}. */ -@RunWith(MockitoJUnitRunner.class) +@RunWith(DelayedInjectionRunner.class) public class ListenerServiceTest { + @DelayedInject private ListenerService listenerService; @Mock @@ -49,25 +48,11 @@ public class ListenerServiceTest { @Mock private PlayerCache playerCache; - @SuppressWarnings("rawtypes") @Before public void initializeTestSetup() { given(settings.getProperty(RegistrationSettings.FORCE)).willReturn(true); given(settings.getProperty(RestrictionSettings.UNRESTRICTED_NAMES)).willReturn( Arrays.asList("npc1", "npc2", "npc3")); - - // Note ljacqu 20160602: We use a hacky way to avoid having to instantiate the service in each test: - // the listenerService test is initialized as a mock that will answer to any method invocation by creating an - // actual service object (with the @Mock fields) and then invoking the method on that actual service. - // As long as there is no interaction with listenerService all of the mock setups will have effect. - listenerService = mock(ListenerService.class, new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Exception { - Method method = invocation.getMethod(); - ListenerService service = new ListenerService(settings, dataSource, pluginHooks, playerCache); - return method.invoke(service, invocation.getArguments()); - } - }); } @Test diff --git a/src/test/java/fr/xephi/authme/task/PurgeServiceTest.java b/src/test/java/fr/xephi/authme/task/PurgeServiceTest.java index b20df325..8e87d91d 100644 --- a/src/test/java/fr/xephi/authme/task/PurgeServiceTest.java +++ b/src/test/java/fr/xephi/authme/task/PurgeServiceTest.java @@ -1,10 +1,11 @@ package fr.xephi.authme.task; +import fr.xephi.authme.DelayedInject; +import fr.xephi.authme.DelayedInjectionRunner; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.hooks.PluginHooks; -import fr.xephi.authme.initialization.FieldInjection; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.settings.NewSetting; @@ -21,15 +22,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import java.lang.reflect.Field; -import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.Set; import java.util.UUID; @@ -54,9 +49,10 @@ import static org.mockito.Mockito.verifyZeroInteractions; /** * Test for {@link PurgeService}. */ -@RunWith(MockitoJUnitRunner.class) +@RunWith(DelayedInjectionRunner.class) public class PurgeServiceTest { + @DelayedInject private PurgeService purgeService; @Mock @@ -88,7 +84,6 @@ public class PurgeServiceTest { given(settings.getProperty(PurgeSettings.USE_AUTO_PURGE)).willReturn(false); // when - initPurgeService(); purgeService.runAutoPurge(); // then @@ -102,7 +97,6 @@ public class PurgeServiceTest { given(settings.getProperty(PurgeSettings.DAYS_BEFORE_REMOVE_PLAYER)).willReturn(0); // when - initPurgeService(); purgeService.runAutoPurge(); // then @@ -120,7 +114,6 @@ public class PurgeServiceTest { mockHasBypassPurgePermission("bravo", "delta"); // when - initPurgeService(); purgeService.runAutoPurge(); // then @@ -140,7 +133,6 @@ public class PurgeServiceTest { CommandSender sender = mock(CommandSender.class); // when - initPurgeService(); purgeService.runPurge(sender, delay); // then @@ -162,7 +154,6 @@ public class PurgeServiceTest { given(sender.getUniqueId()).willReturn(uuid); // when - initPurgeService(); purgeService.runPurge(sender, delay); // then @@ -175,7 +166,6 @@ public class PurgeServiceTest { @Test public void shouldRunPurgeIfProcessIsAlreadyRunning() { // given - initPurgeService(); purgeService.setPurging(true); CommandSender sender = mock(CommandSender.class); OfflinePlayer[] players = mockReturnedOfflinePlayers(); @@ -239,35 +229,4 @@ public class PurgeServiceTest { assertThat(senderInTask, Matchers.equalTo(uuid)); assertThat(namesInTask, containsInAnyOrder(names)); } - - // --------------- - // TODO ljacqu 20160618: Create a delayed injection test runner instead - private void initPurgeService() { - FieldInjection injection = FieldInjection.provide(PurgeService.class).get(); - purgeService = injection.instantiateWith(getFields(injection.getDependencies())); - purgeService.reload(); // because annotated with @PostConstruct - } - - private Object[] getFields(Class[] classes) { - Map, Object> mocksByType = orderMocksByType(); - List orderedMocks = new ArrayList<>(classes.length); - for (Class clazz : classes) { - orderedMocks.add(mocksByType.get(clazz)); - } - return orderedMocks.toArray(); - } - - private Map, Object> orderMocksByType() { - Map, Object> mocksByType = new HashMap<>(); - for (Field field : PurgeServiceTest.class.getDeclaredFields()) { - if (field.isAnnotationPresent(Mock.class)) { - try { - mocksByType.put(field.getType(), field.get(this)); - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } - } - } - return mocksByType; - } } diff --git a/src/test/java/tools/checktestmocks/CheckTestMocks.java b/src/test/java/tools/checktestmocks/CheckTestMocks.java index 2508cdd6..dd6affc9 100644 --- a/src/test/java/tools/checktestmocks/CheckTestMocks.java +++ b/src/test/java/tools/checktestmocks/CheckTestMocks.java @@ -3,9 +3,8 @@ package tools.checktestmocks; import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.Sets; -import fr.xephi.authme.initialization.ConstructorInjection; -import fr.xephi.authme.initialization.FieldInjection; import fr.xephi.authme.initialization.Injection; +import fr.xephi.authme.initialization.InjectionHelper; import fr.xephi.authme.util.StringUtils; import org.mockito.Mock; import tools.utils.AutoToolTask; @@ -139,11 +138,7 @@ public class CheckTestMocks implements AutoToolTask { } private static Set> getRealClassDependencies(Class realClass) { - Injection injection = ConstructorInjection.provide(realClass).get(); - if (injection != null) { - return Sets.newHashSet(injection.getDependencies()); - } - injection = FieldInjection.provide(realClass).get(); + Injection injection = InjectionHelper.getInjection(realClass); return injection == null ? Collections.>emptySet() : Sets.newHashSet(injection.getDependencies()); diff --git a/src/test/java/tools/dependencygraph/DrawDependency.java b/src/test/java/tools/dependencygraph/DrawDependency.java index 4f9dd507..3dc003bd 100644 --- a/src/test/java/tools/dependencygraph/DrawDependency.java +++ b/src/test/java/tools/dependencygraph/DrawDependency.java @@ -5,9 +5,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.converter.Converter; -import fr.xephi.authme.initialization.ConstructorInjection; -import fr.xephi.authme.initialization.FieldInjection; import fr.xephi.authme.initialization.Injection; +import fr.xephi.authme.initialization.InjectionHelper; import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.SynchronousProcess; import fr.xephi.authme.security.crypts.EncryptionMethod; @@ -144,11 +143,7 @@ public class DrawDependency implements ToolTask { } private List getDependencies(Class clazz) { - Injection injection = ConstructorInjection.provide(clazz).get(); - if (injection != null) { - return formatInjectionDependencies(injection); - } - injection = FieldInjection.provide(clazz).get(); + Injection injection = InjectionHelper.getInjection(clazz); return injection == null ? null : formatInjectionDependencies(injection); }