#432 Create custom dependency injector

This commit is contained in:
ljacqu
2016-04-27 22:49:20 +02:00
parent 59d3bc95c0
commit 02079f1f5c
51 changed files with 1347 additions and 278 deletions
@@ -18,10 +18,10 @@ import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.ValidationService;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@@ -41,6 +41,7 @@ import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class CommandServiceTest {
@InjectMocks
private CommandService commandService;
@Mock
private AuthMe authMe;
@@ -67,12 +68,6 @@ public class CommandServiceTest {
@Mock
private BukkitService bukkitService;
@Before
public void setUpService() {
commandService = new CommandService(authMe, commandMapper, helpProvider, messages, passwordSecurity,
permissionsManager, settings, pluginHooks, spawnLoader, antiBot, validationService, bukkitService);
}
@Test
public void shouldSendMessage() {
// given
@@ -6,6 +6,8 @@ import fr.xephi.authme.command.FoundResultStatus;
import fr.xephi.authme.command.TestCommandsUtil;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerPermission;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.properties.PluginSettings;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.junit.Before;
@@ -52,7 +54,9 @@ public class HelpProviderTest {
@Before
public void setUpHelpProvider() {
permissionsManager = mock(PermissionsManager.class);
helpProvider = new HelpProvider(permissionsManager, HELP_HEADER);
NewSetting settings = mock(NewSetting.class);
given(settings.getProperty(PluginSettings.HELP_HEADER)).willReturn(HELP_HEADER);
helpProvider = new HelpProvider(permissionsManager, settings);
sender = mock(CommandSender.class);
}
@@ -0,0 +1,194 @@
package fr.xephi.authme.initialization;
import fr.xephi.authme.initialization.samples.BadFieldInjection;
import fr.xephi.authme.initialization.samples.BetaManager;
import fr.xephi.authme.initialization.samples.CircularClasses;
import fr.xephi.authme.initialization.samples.ClassWithAbstractDependency;
import fr.xephi.authme.initialization.samples.ClassWithAnnotations;
import fr.xephi.authme.initialization.samples.Duration;
import fr.xephi.authme.initialization.samples.FieldInjectionWithAnnotations;
import fr.xephi.authme.initialization.samples.InvalidClass;
import fr.xephi.authme.initialization.samples.InvalidPostConstruct;
import fr.xephi.authme.initialization.samples.PostConstructTestClass;
import fr.xephi.authme.initialization.samples.ProvidedClass;
import fr.xephi.authme.initialization.samples.Size;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
/**
* Test for {@link AuthMeServiceInitializer}.
*/
public class AuthMeServiceInitializerTest {
private static final String ALLOWED_PACKAGE = "fr.xephi.authme.initialization";
private AuthMeServiceInitializer initializer;
@Before
public void setInitializer() {
initializer = new AuthMeServiceInitializer(ALLOWED_PACKAGE);
initializer.register(new ProvidedClass(""));
}
@Test
public void shouldInitializeElements() {
// given / when
BetaManager betaManager = initializer.get(BetaManager.class);
// then
assertThat(betaManager, not(nullValue()));
for (Object o : betaManager.getDependencies()) {
assertThat(o, not(nullValue()));
}
}
@Test(expected = IllegalStateException.class)
public void shouldThrowForInvalidPackage() {
// given / when / then
initializer.get(InvalidClass.class);
}
@Test
public void shouldPassValueByAnnotation() {
// given
int size = 12;
long duration = -15482L;
initializer.provide(Size.class, size);
initializer.provide(Duration.class, duration);
// when
ClassWithAnnotations object = initializer.get(ClassWithAnnotations.class);
// then
assertThat(object, not(nullValue()));
assertThat(object.getSize(), equalTo(size));
assertThat(object.getDuration(), equalTo(duration));
// some sample check to make sure we only have one instance of GammaService
assertThat(object.getGammaService(), equalTo(initializer.get(BetaManager.class).getDependencies()[1]));
}
@Test(expected = RuntimeException.class)
public void shouldRecognizeCircularReferences() {
// given / when / then
initializer.get(CircularClasses.Circular3.class);
}
@Test(expected = RuntimeException.class)
public void shouldThrowForUnregisteredAnnotation() {
// given
initializer.provide(Size.class, 4523);
// when / then
initializer.get(ClassWithAnnotations.class);
}
@Test(expected = RuntimeException.class)
public void shouldThrowForFieldInjectionWithNoDefaultConstructor() {
// given / when / then
initializer.get(BadFieldInjection.class);
}
@Test
public void shouldInjectFieldsWithAnnotationsProperly() {
// given
initializer.provide(Size.class, 2809375);
initializer.provide(Duration.class, 13095L);
// when
FieldInjectionWithAnnotations result = initializer.get(FieldInjectionWithAnnotations.class);
// then
assertThat(result.getSize(), equalTo(2809375));
assertThat(result.getDuration(), equalTo(13095L));
assertThat(result.getBetaManager(), not(nullValue()));
assertThat(result.getClassWithAnnotations(), not(nullValue()));
assertThat(result.getClassWithAnnotations().getGammaService(),
equalTo(result.getBetaManager().getDependencies()[1]));
}
@Test(expected = RuntimeException.class)
public void shouldThrowForAnnotationAsKey() {
// given / when / then
initializer.get(Size.class);
}
@Test(expected = RuntimeException.class)
public void shouldThrowForSecondRegistration() {
// given / when / then
initializer.register(new ProvidedClass(""));
}
@Test(expected = RuntimeException.class)
public void shouldThrowForSecondAnnotationRegistration() {
// given
initializer.provide(Size.class, 12);
// when / then
initializer.provide(Size.class, -8);
}
@Test(expected = NullPointerException.class)
public void shouldThrowForNullValueAssociatedToAnnotation() {
// given / when / then
initializer.provide(Duration.class, null);
}
@Test(expected = NullPointerException.class)
public void shouldThrowForRegisterWithNull() {
// given / when / then
initializer.register(String.class, null);
}
@Test(expected = RuntimeException.class)
public void shouldThrowForRegisterOfType() {
// given / when / then
// this most likely means that the second argument was forgotten, so throw an error and force
// the API user to use the explicit register(Class.class, String.class) if really, really desired
// (Though for such generic types, an annotation would be a lot better)
initializer.register(String.class);
}
@Test
public void shouldExecutePostConstructMethod() {
// given
initializer.provide(Size.class, 15123);
// when
PostConstructTestClass testClass = initializer.get(PostConstructTestClass.class);
// then
assertThat(testClass.werePostConstructsCalled(), equalTo(true));
assertThat(testClass.getBetaManager(), not(nullValue()));
}
@Test(expected = RuntimeException.class)
public void shouldThrowForInvalidPostConstructMethod() {
// given / when / then
initializer.get(InvalidPostConstruct.class);
}
@Test(expected = RuntimeException.class)
public void shouldThrowForAbstractNonRegisteredDependency() {
// given / when / then
initializer.get(ClassWithAbstractDependency.class);
}
@Test
public void shouldInstantiateWithImplementationOfAbstractDependency() {
// given
ClassWithAbstractDependency.ConcreteDependency concrete = new ClassWithAbstractDependency.ConcreteDependency();
initializer.register(ClassWithAbstractDependency.AbstractDependency.class, concrete);
// when
ClassWithAbstractDependency cwad = initializer.get(ClassWithAbstractDependency.class);
// then
assertThat(cwad.getAbstractDependency() == concrete, equalTo(true));
assertThat(cwad.getAlphaService(), not(nullValue()));
}
}
@@ -0,0 +1,20 @@
package fr.xephi.authme.initialization.samples;
import javax.inject.Inject;
/**
* Sample - class with dependency to ProvidedClass.
*/
public class AlphaService {
private ProvidedClass providedClass;
@Inject
AlphaService(ProvidedClass providedClass) {
this.providedClass = providedClass;
}
public ProvidedClass getProvidedClass() {
return providedClass;
}
}
@@ -0,0 +1,16 @@
package fr.xephi.authme.initialization.samples;
import javax.inject.Inject;
/**
* Sample class with invalid field injection (requires default constructor).
*/
public class BadFieldInjection {
@Inject
private AlphaService alphaService;
public BadFieldInjection(BetaManager betaManager) {
throw new IllegalStateException("Should never be called");
}
}
@@ -0,0 +1,20 @@
package fr.xephi.authme.initialization.samples;
import javax.inject.Inject;
/**
* Sample - depends on Provided, alpha and gamma.
*/
public class BetaManager {
@Inject
private ProvidedClass providedClass;
@Inject
private GammaService gammaService;
@Inject
private AlphaService alphaService;
public Object[] getDependencies() {
return new Object[]{providedClass, gammaService, alphaService};
}
}
@@ -0,0 +1,30 @@
package fr.xephi.authme.initialization.samples;
import javax.inject.Inject;
/**
* Classes with circular dependencies.
*/
public class CircularClasses {
public static final class Circular1 {
@Inject
public Circular1(AlphaService alphaService, Circular3 circular3) {
// --
}
}
public static final class Circular2 {
@Inject
public Circular2(Circular1 circular1) {
// --
}
}
public static final class Circular3 {
@Inject
public Circular3(Circular2 circular2, BetaManager betaManager) {
// --
}
}
}
@@ -0,0 +1,32 @@
package fr.xephi.authme.initialization.samples;
import javax.inject.Inject;
/**
* Test with an abstract class declared as dependency.
*/
public class ClassWithAbstractDependency {
private final AlphaService alphaService;
private final AbstractDependency abstractDependency;
@Inject
public ClassWithAbstractDependency(AlphaService as, AbstractDependency ad) {
this.alphaService = as;
this.abstractDependency = ad;
}
public AlphaService getAlphaService() {
return alphaService;
}
public AbstractDependency getAbstractDependency() {
return abstractDependency;
}
public static abstract class AbstractDependency {
}
public static final class ConcreteDependency extends AbstractDependency {
}
}
@@ -0,0 +1,29 @@
package fr.xephi.authme.initialization.samples;
import javax.inject.Inject;
public class ClassWithAnnotations {
private int size;
private GammaService gammaService;
private long duration;
@Inject
ClassWithAnnotations(@Size int size, GammaService gammaService, @Duration long duration) {
this.size = size;
this.gammaService = gammaService;
this.duration = duration;
}
public int getSize() {
return size;
}
public GammaService getGammaService() {
return gammaService;
}
public long getDuration() {
return duration;
}
}
@@ -0,0 +1,14 @@
package fr.xephi.authme.initialization.samples;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Sample annotation.
*/
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Duration {
}
@@ -0,0 +1,40 @@
package fr.xephi.authme.initialization.samples;
import javax.inject.Inject;
/**
* Sample - field injection, including custom annotations.
*/
public class FieldInjectionWithAnnotations {
@Inject
private BetaManager betaManager;
@Inject
@Size
private int size;
@Duration
@Inject
private long duration;
@Inject
protected ClassWithAnnotations classWithAnnotations;
FieldInjectionWithAnnotations() {
}
public BetaManager getBetaManager() {
return betaManager;
}
public int getSize() {
return size;
}
public long getDuration() {
return duration;
}
public ClassWithAnnotations getClassWithAnnotations() {
return classWithAnnotations;
}
}
@@ -0,0 +1,20 @@
package fr.xephi.authme.initialization.samples;
import javax.inject.Inject;
/**
* Sample - class dependent on alpha and provided.
*/
public class GammaService {
private AlphaService alphaService;
@Inject
GammaService(AlphaService alphaService) {
this.alphaService = alphaService;
}
public AlphaService getAlphaService() {
return alphaService;
}
}
@@ -0,0 +1,14 @@
package fr.xephi.authme.initialization.samples;
import javax.inject.Inject;
/**
* Sample - invalid class, since Integer parameter type is outside of the allowed package and not annotated.
*/
public class InvalidClass {
@Inject
public InvalidClass(AlphaService alphaService, Integer i) {
throw new IllegalStateException("Should never be called");
}
}
@@ -0,0 +1,19 @@
package fr.xephi.authme.initialization.samples;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
/**
* Class with invalid @PostConstruct method.
*/
public class InvalidPostConstruct {
@Inject
private AlphaService alphaService;
@Inject
private ProvidedClass providedClass;
@PostConstruct
public void invalidPostConstr(BetaManager betaManager) {
}
}
@@ -0,0 +1,37 @@
package fr.xephi.authme.initialization.samples;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
/**
* Sample class for testing the execution of the @PostConstruct method.
*/
public class PostConstructTestClass {
@Inject
@Size
private int size;
@Inject
private BetaManager betaManager;
private boolean wasPostConstructCalled = false;
private boolean wasSecondPostConstructCalled = false;
@PostConstruct
protected void setFieldToTrue() {
wasPostConstructCalled = true;
}
@PostConstruct
public int otherPostConstructMethod() {
wasSecondPostConstructCalled = true;
return 42;
}
public boolean werePostConstructsCalled() {
return wasPostConstructCalled && wasSecondPostConstructCalled;
}
public BetaManager getBetaManager() {
return betaManager;
}
}
@@ -0,0 +1,18 @@
package fr.xephi.authme.initialization.samples;
import javax.inject.Inject;
/**
* Sample - class that is always provided to the initializer beforehand.
*/
public class ProvidedClass {
@Inject
public ProvidedClass() {
throw new IllegalStateException("Should never be called (tests always provide this class)");
}
public ProvidedClass(String manualConstructor) {
}
}
@@ -0,0 +1,14 @@
package fr.xephi.authme.initialization.samples;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Sample annotation.
*/
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Size {
}
@@ -13,9 +13,9 @@ import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.ValidationService;
import org.bukkit.command.CommandSender;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class ProcessServiceTest {
@InjectMocks
private ProcessService processService;
@Mock
private ValidationService validationService;
@@ -51,12 +52,6 @@ public class ProcessServiceTest {
@Mock
private BukkitService bukkitService;
@Before
public void setUpService() {
processService = new ProcessService(settings, messages, authMe, dataSource, passwordSecurity,
pluginHooks, spawnLoader, validationService, bukkitService);
}
@Test
public void shouldGetProperty() {
// given