diff --git a/src/main/java/org/fest/reflect/simple/SimpleInjector.java b/src/main/java/org/fest/reflect/simple/SimpleInjector.java new file mode 100644 index 0000000..cfa9263 --- /dev/null +++ b/src/main/java/org/fest/reflect/simple/SimpleInjector.java @@ -0,0 +1,79 @@ +package org.fest.reflect.simple; + +import static java.text.MessageFormat.format; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +/** + * Provides simple injection capabilities when needed in tests. To be used by {@link SimpleReflection}. + * + * @author Marek Dominiak + */ +public class SimpleInjector { + private final Object valueToSet; + + static SimpleInjector set(Object value) { + return new SimpleInjector(value); + } + + private SimpleInjector(Object value) { + this.valueToSet = value; + } + + /** + * Sets {@link #valueToSet} into target object if it is possible. + * Rules for setting: + *
+ * Here are some examples: + *
+ * // import static {@link org.fest.reflect.simple.SimpleReflection org.fest.reflect.simple.SimpleReflection}.*; + * + * // Retrieves the value of the field "logger" from object userService + * UserService userService = ... + * Logger logger = SimpleReflection.get(Logger.class).from(userService); + * + * // Sets the value of the field 'logger' to "Yoda" + * UserService userService = ... + * Logger logger = ... + * Logger logger = SimpleReflection.set(logger).to(userService); + *+ * + * @author Marek Dominiak + */ +public class SimpleReflection { + /** + * Begins the setting/injecting of the valueToSet to some object. + * @param valueToSet + * @return fluent API interface to set/inject valueToSet to some object. + */ + public static SimpleInjector set(Object valueToSet) { + return SimpleInjector.set(valueToSet); + } + + /** + * Begins the getting/retrieving of the field value of the provided type from some object. + * @param fieldValueType + * @return fluent API interface to get/retrieve field value of the provided type from some object. + */ + public static
+Provides a "fluent" API that +makes usage of the Java Reflection API to help achieve some common case scenarios when setting/retrieving state of the objects in tests. +
++Here are some examples: +
+ // import static org.fest.reflect.simple.SimpleReflection.*; + + // Retrieves the value of the field "logger" from object userService + UserService userService = ... + Logger logger = SimpleReflection.get(Logger.class).from(userService); + + // Sets the value of the field 'logger' to "Yoda" + UserService userService = ... + Logger logger = ... + Logger logger = SimpleReflection.set(logger).to(userService); ++ + + \ No newline at end of file diff --git a/src/test/java/org/fest/reflect/simple/SimpleReflectionTest.java b/src/test/java/org/fest/reflect/simple/SimpleReflectionTest.java new file mode 100644 index 0000000..2600295 --- /dev/null +++ b/src/test/java/org/fest/reflect/simple/SimpleReflectionTest.java @@ -0,0 +1,167 @@ +package org.fest.reflect.simple; + +import junit.framework.Assert; + +import org.fest.test.CodeToTest; +import org.fest.test.ExpectedFailure; +import org.junit.Test; + +public class SimpleReflectionTest { + + @SuppressWarnings("unused") + class ClassWithTwoFieldsOfTheSameType { + private Crowdy crowdy; + private Crowdy reallyCrowdy; + public ClassWithTwoFieldsOfTheSameType(Crowdy crowdy, Crowdy reallyCrowdy) { + super(); + this.crowdy = crowdy; + this.reallyCrowdy = reallyCrowdy; + } + public ClassWithTwoFieldsOfTheSameType() { + super(); + } + } + + class ClassWithExactlyOneField { + private Logger logger; + + public Logger getLogger() { + return logger; + } + } + + class ClassWithExactlyOneCompatibleField { + private SomeInterface someInterface; + + public SomeInterface getSomeInterface() { + return someInterface; + } + } + + class CompletelyEmpty { + } + + class Logger { + } + + interface SomeInterface { + } + + class ClassWithInterface implements SomeInterface { + } + + class Crowdy { + } + + @Test + public void shouldInjectFieldByTypeInAnObject() throws Exception { + // given + ClassWithExactlyOneField exactlyOneField = new ClassWithExactlyOneField(); + Logger logger = new Logger(); + + // when + SimpleReflection.set(logger).in(exactlyOneField); + + // then + Assert.assertEquals(logger, exactlyOneField.getLogger()); + } + + @Test + public void shouldInjectFieldByTypeInAnObjectWhenTheInjectedValueIfOfTypeExtendingTypeOfTargetField() + throws Exception { + // given + ClassWithExactlyOneCompatibleField exactlyOneCompatibleField = new ClassWithExactlyOneCompatibleField(); + ClassWithInterface classWithInterface = new ClassWithInterface(); + + // when + SimpleReflection.set(classWithInterface).in(exactlyOneCompatibleField); + + // then + Assert.assertEquals(classWithInterface, exactlyOneCompatibleField.getSomeInterface()); + } + + @Test + public void shouldThrowISEWhenInjectingValueOfTypeWhenThereAreNoneFieldsOfThisTypeInTargetObject() throws Exception { + final CompletelyEmpty completelyEmpty = new CompletelyEmpty(); + final Logger logger = new Logger(); + ExpectedFailure + .expect(IllegalStateException.class) + .withMessage( + "There must be exactly ONE field of type " + Logger.class.getName() + " (or assignable from "+ Logger.class.getName() + ") in target class " + + CompletelyEmpty.class.getName() + "!\nBut found none.").on(new CodeToTest() { + public void run() throws Throwable { + SimpleReflection.set(logger).in(completelyEmpty); + } + }); + } + + @Test + public void shouldThrowISEWhenInjectingValueOfTypeWhenThereAreMoreFieldsOfThisTypeInTargetObject() throws Exception { + final Crowdy crowdy = new Crowdy(); + final ClassWithTwoFieldsOfTheSameType classWithTwoFieldsOfTheSameType = new ClassWithTwoFieldsOfTheSameType(); + ExpectedFailure + .expect(IllegalStateException.class) + .withMessage( + "There must be exactly ONE field of type " + Crowdy.class.getName() + " (or assignable from " + Crowdy.class.getName() + ") in target class " + + ClassWithTwoFieldsOfTheSameType.class.getName() + "!\nBut found 2:\ncrowdy\nreallyCrowdy\n") + .on(new CodeToTest() { + public void run() throws Throwable { + SimpleReflection.set(crowdy).in(classWithTwoFieldsOfTheSameType); + } + }); + } + + @Test + public void shouldRetrieveFieldByTypeInAnObjectWhenOnlyOneFieldOfThisTypeExist() throws Exception { + // given + ClassWithExactlyOneField exactlyOneField = new ClassWithExactlyOneField(); + Logger logger = new Logger(); + SimpleReflection.set(logger).in(exactlyOneField); + + // when + Logger result = SimpleReflection.get(Logger.class).from(exactlyOneField); + // then + Assert.assertEquals(logger, result); + } + + @Test + public void shouldRetrieveFieldValueFromSourceObjectByTypeWhenSourceClassHasCompatibleField() throws Exception { + // given + // compatible = from the inheritance hierarchy + ClassWithExactlyOneCompatibleField classWithExactlyOneCompatibleField = new ClassWithExactlyOneCompatibleField(); + ClassWithInterface value = new ClassWithInterface(); + SimpleReflection.set(value).in(classWithExactlyOneCompatibleField); + // when + SomeInterface result = SimpleReflection.get(SomeInterface.class).from(classWithExactlyOneCompatibleField); + // then + Assert.assertEquals(value, result); + } + + @Test + public void shouldThrowISEWhenTryingToGetFieldByTypeInAnObjectWhenThereAreMoreFieldsOfThisTypeInSourceObject() throws Exception { + // given + Crowdy crowdy1 = new Crowdy(); + Crowdy crowdy2 = new Crowdy(); + final ClassWithTwoFieldsOfTheSameType source = new ClassWithTwoFieldsOfTheSameType(crowdy1, crowdy2); + ExpectedFailure + .expect(IllegalStateException.class) + .withMessage("Can't retrieve value of type: " + Crowdy.class.getName() + " because there are more fields of this type in source bean (" + ClassWithTwoFieldsOfTheSameType.class.getName() + ")").on(new CodeToTest() { + public void run() throws Throwable { + SimpleReflection.get(Crowdy.class).from(source); + } + }); + } + + @Test + public void shouldThrowISEWhenTryingToGetFieldByTypeInAnObjectWhichDoesNotDefineFieldOfThisType() throws Exception { + // given + final CompletelyEmpty source = new CompletelyEmpty(); + ExpectedFailure + .expect(IllegalStateException.class) + .withMessage("Can't retrieve value of type: " + Crowdy.class.getName() + " because there none fields of this type (or compatible type) in source bean (" + CompletelyEmpty.class.getName() + ")").on(new CodeToTest() { + public void run() throws Throwable { + SimpleReflection.get(Crowdy.class).from(source); + } + }); + } +}