mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-12-24 17:47:38 +01:00
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
This commit is contained in:
parent
cd1acfde1b
commit
a1c62e7c04
@ -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> T instantiate(Class<T> clazz, Set<Class<?>> traversedClasses) {
|
||||
Injection<T> injection = firstNotNull(
|
||||
ConstructorInjection.provide(clazz), FieldInjection.provide(clazz), InstantiationFallback.provide(clazz));
|
||||
Injection<T> 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 <T> Injection<T> firstNotNull(Provider<? extends Injection<T>>... providers) {
|
||||
for (Provider<? extends Injection<T>> provider : providers) {
|
||||
Injection<T> object = provider.get();
|
||||
if (object != null) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
/**
|
||||
* Functionality for constructor injection.
|
||||
*/
|
||||
public class ConstructorInjection<T> implements Injection<T> {
|
||||
class ConstructorInjection<T> implements Injection<T> {
|
||||
|
||||
private final Constructor<T> constructor;
|
||||
|
||||
|
@ -16,7 +16,7 @@ import java.util.List;
|
||||
/**
|
||||
* Functionality for field injection.
|
||||
*/
|
||||
public class FieldInjection<T> implements Injection<T> {
|
||||
class FieldInjection<T> implements Injection<T> {
|
||||
|
||||
private final Field[] fields;
|
||||
private final Constructor<T> defaultConstructor;
|
||||
|
@ -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 <T> the class' type
|
||||
* @return injection of the class or null if none detected
|
||||
*/
|
||||
public static <T> Injection<T> getInjection(Class<T> 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 <T> Injection<T> firstNotNull(Provider<? extends Injection<T>>... providers) {
|
||||
for (Provider<? extends Injection<T>> provider : providers) {
|
||||
Injection<T> object = provider.get();
|
||||
if (object != null) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -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<T> implements Injection<T> {
|
||||
class InstantiationFallback<T> implements Injection<T> {
|
||||
|
||||
private final Constructor<T> constructor;
|
||||
|
||||
|
16
src/test/java/fr/xephi/authme/DelayedInject.java
Normal file
16
src/test/java/fr/xephi/authme/DelayedInject.java
Normal file
@ -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 {
|
||||
}
|
154
src/test/java/fr/xephi/authme/DelayedInjectionRunner.java
Normal file
154
src/test/java/fr/xephi/authme/DelayedInjectionRunner.java
Normal file
@ -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}.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<FrameworkField> delayedFields = getTestClass().getAnnotatedFields(InjectMocks.class);
|
||||
for (FrameworkField field : delayedFields) {
|
||||
Object o = ReflectionTestUtils.getFieldValue(field.getField(), target);
|
||||
executePostConstructMethod(o);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeDelayedMocks(Object target) {
|
||||
List<FrameworkField> 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<FrameworkField> availableMocks = getTestClass().getAnnotatedFields(Mock.class);
|
||||
Map<Class<?>, 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<Object> {
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 <T> Field getField(Class<T> 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 <T> Object getFieldValue(Class<T> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<CommandDescription> 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);
|
||||
}
|
||||
|
||||
// -----------
|
||||
|
@ -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
|
||||
|
@ -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.<Object>equalTo(uuid));
|
||||
assertThat(namesInTask, containsInAnyOrder(names));
|
||||
}
|
||||
|
||||
// ---------------
|
||||
// TODO ljacqu 20160618: Create a delayed injection test runner instead
|
||||
private void initPurgeService() {
|
||||
FieldInjection<PurgeService> injection = FieldInjection.provide(PurgeService.class).get();
|
||||
purgeService = injection.instantiateWith(getFields(injection.getDependencies()));
|
||||
purgeService.reload(); // because annotated with @PostConstruct
|
||||
}
|
||||
|
||||
private Object[] getFields(Class<?>[] classes) {
|
||||
Map<Class<?>, Object> mocksByType = orderMocksByType();
|
||||
List<Object> orderedMocks = new ArrayList<>(classes.length);
|
||||
for (Class<?> clazz : classes) {
|
||||
orderedMocks.add(mocksByType.get(clazz));
|
||||
}
|
||||
return orderedMocks.toArray();
|
||||
}
|
||||
|
||||
private Map<Class<?>, Object> orderMocksByType() {
|
||||
Map<Class<?>, 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<Class<?>> 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.<Class<?>>emptySet()
|
||||
: Sets.newHashSet(injection.getDependencies());
|
||||
|
@ -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<String> 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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user