package br.com.caelum.iogi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.util.Collections; import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.After; import org.junit.Before; import org.junit.Test; import br.com.caelum.iogi.fixtures.OneConstructibleArgument; import br.com.caelum.iogi.fixtures.OneIntegerPrimitive; import br.com.caelum.iogi.fixtures.OneStringOneConstructible; import br.com.caelum.iogi.parameters.Parameter; import br.com.caelum.iogi.parameters.Parameters; import br.com.caelum.iogi.reflection.ParanamerParameterNamesProvider; import br.com.caelum.iogi.reflection.Target; import br.com.caelum.iogi.spi.DependencyProvider; import br.com.caelum.iogi.util.DefaultLocaleProvider; import br.com.caelum.iogi.util.NullDependencyProvider; public class ObjectInstantiatorTests { private Mockery context; private Instantiator<Object> stubInstantiator; @SuppressWarnings("unchecked") @Before public void setUp() { context = new Mockery(); stubInstantiator = context.mock(Instantiator.class); context.checking(new Expectations() {{ allowing(stubInstantiator).isAbleToInstantiate(with(any(Target.class))); will(returnValue(true)); allowing(stubInstantiator).instantiate(with(any(Target.class)), with(any(Parameters.class))); will(returnValue("x")); }}); } @After public void tearDown() { context.assertIsSatisfied(); } @Test public void canInstantiateIfThereAreExtraParametersInTheRequest() throws Exception { final Target<ConstructorAndProperty> target = Target.create(ConstructorAndProperty.class, "root"); final Parameter paramFoundInConstructor = new Parameter("root.constructorArg", "x"); final Parameter paramFoundSetter = new Parameter("root.propertyValue", "x"); final ObjectInstantiator objectInstantiator = new ObjectInstantiator(stubInstantiator, new NullDependencyProvider(), new ParanamerParameterNamesProvider()); final Object object = objectInstantiator.instantiate(target, new Parameters(paramFoundInConstructor, paramFoundSetter)); assertNotNull(object); } @Test public void willFallbackToSetterIfNoAppropriateConstructorIsFound() throws Exception { final Target<ConstructorAndProperty> target = Target.create(ConstructorAndProperty.class, "root"); final Parameter paramFoundInConstructor = new Parameter("root.constructorArg", "x"); final Parameter paramFoundSetter = new Parameter("root.propertyValue", "x"); final ObjectInstantiator objectInstantiator = new ObjectInstantiator(stubInstantiator, new NullDependencyProvider(), new ParanamerParameterNamesProvider()); final ConstructorAndProperty object = (ConstructorAndProperty) objectInstantiator.instantiate(target, new Parameters(paramFoundInConstructor, paramFoundSetter)); assertEquals("x", object.getConstructorArg()); assertEquals("x", object.getPropertyValue()); } @Test public void ifThereIsMoreThanOneCompatibleConstructorPickTheLargestOne() throws Exception { final Target<TwoCompatibleConstructors> target = Target.create(TwoCompatibleConstructors.class, "root"); final Parameter a = new Parameter("root.a", "x"); final Parameter b = new Parameter("root.b", "x"); final Parameter c = new Parameter("root.c", "x"); final Parameter irrelevant = new Parameter("root.irrelevant", "x"); final ObjectInstantiator objectInstantiator = new ObjectInstantiator(stubInstantiator, new NullDependencyProvider(), new ParanamerParameterNamesProvider()); final TwoCompatibleConstructors object = (TwoCompatibleConstructors) objectInstantiator.instantiate(target, new Parameters(a, b, c, irrelevant)); assertTrue(object.largestWasCalled); } @Test public void willCallDependencyInjectorForUninstantiabelParameters() throws Exception { final Target<OneConstructibleArgument> rootTarget = Target.create(OneConstructibleArgument.class, "root"); final DependencyProvider mockDependencyProvider = context.mock(DependencyProvider.class); final Target<OneIntegerPrimitive> argTarget = Target.create(OneIntegerPrimitive.class, "arg"); final OneIntegerPrimitive injectedValue = new OneIntegerPrimitive(47); context.checking(new Expectations() {{ atLeast(1).of(mockDependencyProvider).canProvide(with(equal(argTarget))); will(returnValue(true)); atLeast(1).of(mockDependencyProvider).provide(argTarget); will(returnValue(injectedValue)); }}); final ObjectInstantiator objectInstantiator = new ObjectInstantiator(stubInstantiator, mockDependencyProvider, new ParanamerParameterNamesProvider()); final OneConstructibleArgument object = (OneConstructibleArgument) objectInstantiator.instantiate(rootTarget, new Parameters(Collections.<Parameter>emptyList())); assertSame(injectedValue, object.getArg()); } @Test public void willCallDependencyInjectorForUninstantiabelParametersAlongsideInstantiablaParameters() { final DependencyProvider mockDependencyProvider = context.mock(DependencyProvider.class); final Target<OneStringOneConstructible> rootTarget = Target.create(OneStringOneConstructible.class, "root"); final Parameters parameters = new Parameters(new Parameter("root.one", "x")); final Target<OneIntegerPrimitive> injectableTarget = Target.create(OneIntegerPrimitive.class, "two"); final OneIntegerPrimitive injectedValue = new OneIntegerPrimitive(47); context.checking(new Expectations() {{ allowing(mockDependencyProvider).canProvide(with(equal(injectableTarget))); will(returnValue(true)); atLeast(1).of(mockDependencyProvider).provide(injectableTarget); will(returnValue(injectedValue)); }}); final ObjectInstantiator objectInstantiator = new ObjectInstantiator(stubInstantiator, mockDependencyProvider, new ParanamerParameterNamesProvider()); final OneStringOneConstructible object = (OneStringOneConstructible) objectInstantiator.instantiate(rootTarget, parameters); assertSame(injectedValue, object.getTwo()); assertEquals("x", object.getOne()); } @Test public void shouldInstantiateRecursiveArgumentsGuidedByTheParameters() throws Exception { Iogi iogi = new Iogi(new NullDependencyProvider(), new DefaultLocaleProvider()); Recursive newObject = iogi.instantiate(Target.create(Recursive.class, "target"), new Parameters(new Parameter("target.r.s" , "asdf"), new Parameter("target.s", "asdf"))); assertNotNull(newObject.r); } @Test public void willUseScalaSetters() throws Exception { final Target<ScalaObject> target = Target.create(ScalaObject.class, "root"); final Parameter paramFoundInConstructor = new Parameter("root.constructorArg", "x"); final Parameter paramFoundSetter = new Parameter("root.propertyValue", "x"); final ObjectInstantiator objectInstantiator = new ObjectInstantiator(stubInstantiator, new NullDependencyProvider(), new ParanamerParameterNamesProvider()); final ScalaObject object = (ScalaObject) objectInstantiator.instantiate(target, new Parameters(paramFoundInConstructor, paramFoundSetter)); assertEquals("x", object.constructorArg()); assertEquals("x", object.propertyValue()); } public static class Recursive { Recursive r; public Recursive(String s) { } public Recursive(Recursive r, String s) { this.r = r; } } public static class TwoCompatibleConstructors { boolean largestWasCalled = false; public TwoCompatibleConstructors(final String a, final String b) { } public TwoCompatibleConstructors(final String a, final String b, final String c) { largestWasCalled = true; } } public static class ConstructorAndProperty { private final String constructorArg; private String propertyValue; public ConstructorAndProperty(final String constructorArg) { this.constructorArg = constructorArg; } public String getConstructorArg() { return constructorArg; } public String getPropertyValue() { return propertyValue; } public void setPropertyValue(final String propertyValue) { this.propertyValue = propertyValue; } } public static class ScalaObject { private final String constructorArg; private String propertyValue; public ScalaObject(final String constructorArg) { this.constructorArg = constructorArg; } public String constructorArg() { return constructorArg; } public String propertyValue() { return propertyValue; } public void propertyValue_$eq(final String propertyValue) { this.propertyValue = propertyValue; } } }