/** * Copyright (C) 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.inject; import static com.google.inject.Asserts.assertContains; import com.google.inject.name.Names; import com.google.inject.util.Providers; import junit.framework.AssertionFailedError; import junit.framework.TestCase; import java.util.concurrent.atomic.AtomicReference; /** * @author jessewilson@google.com (Jesse Wilson) */ public class MembersInjectorTest extends TestCase { private static final A<C> uninjectableA = new A<C>() { @Override void doNothing() { throw new AssertionFailedError(); } }; private static final B uninjectableB = new B() { @Override void doNothing() { throw new AssertionFailedError(); } }; private static final C myFavouriteC = new C(); public void testMembersInjectorFromBinder() { final AtomicReference<MembersInjector<A<C>>> aMembersInjectorReference = new AtomicReference<MembersInjector<A<C>>>(); final AtomicReference<MembersInjector<B>> bMembersInjectorReference = new AtomicReference<MembersInjector<B>>(); Guice.createInjector(new AbstractModule() { @Override protected void configure() { MembersInjector<A<C>> aMembersInjector = getMembersInjector(new TypeLiteral<A<C>>() {}); try { aMembersInjector.injectMembers(uninjectableA); fail(); } catch (IllegalStateException expected) { assertContains(expected.getMessage(), "This MembersInjector cannot be used until the Injector has been created."); } MembersInjector<B> bMembersInjector = getMembersInjector(B.class); try { bMembersInjector.injectMembers(uninjectableB); fail(); } catch (IllegalStateException expected) { assertContains(expected.getMessage(), "This MembersInjector cannot be used until the Injector has been created."); } aMembersInjectorReference.set(aMembersInjector); bMembersInjectorReference.set(bMembersInjector); assertEquals("MembersInjector<java.lang.String>", getMembersInjector(String.class).toString()); bind(C.class).toInstance(myFavouriteC); } }); A<C> injectableA = new A<C>(); aMembersInjectorReference.get().injectMembers(injectableA); assertSame(myFavouriteC, injectableA.t); assertSame(myFavouriteC, injectableA.b.c); B injectableB = new B(); bMembersInjectorReference.get().injectMembers(injectableB); assertSame(myFavouriteC, injectableB.c); B anotherInjectableB = new B(); bMembersInjectorReference.get().injectMembers(anotherInjectableB); assertSame(myFavouriteC, anotherInjectableB.c); } public void testMembersInjectorFromInjector() { Injector injector = Guice.createInjector(new AbstractModule() { protected void configure() { bind(C.class).toInstance(myFavouriteC); } }); MembersInjector<A<C>> aMembersInjector = injector.getMembersInjector(new TypeLiteral<A<C>>() {}); MembersInjector<B> bMembersInjector = injector.getMembersInjector(B.class); A<C> injectableA = new A<C>(); aMembersInjector.injectMembers(injectableA); assertSame(myFavouriteC, injectableA.t); assertSame(myFavouriteC, injectableA.b.c); B injectableB = new B(); bMembersInjector.injectMembers(injectableB); assertSame(myFavouriteC, injectableB.c); B anotherInjectableB = new B(); bMembersInjector.injectMembers(anotherInjectableB); assertSame(myFavouriteC, anotherInjectableB.c); assertEquals("MembersInjector<java.lang.String>", injector.getMembersInjector(String.class).toString()); } public void testMembersInjectorWithNonInjectedTypes() { Injector injector = Guice.createInjector(); MembersInjector<NoInjectedMembers> membersInjector = injector.getMembersInjector(NoInjectedMembers.class); membersInjector.injectMembers(new NoInjectedMembers()); membersInjector.injectMembers(new NoInjectedMembers()); } public void testInjectionFailure() { Injector injector = Guice.createInjector(); MembersInjector<InjectionFailure> membersInjector = injector.getMembersInjector(InjectionFailure.class); try { membersInjector.injectMembers(new InjectionFailure()); fail(); } catch (ProvisionException expected) { assertContains(expected.getMessage(), "1) Error injecting method, java.lang.ClassCastException: whoops, failure #1"); } } public void testInjectionAppliesToSpecifiedType() { Injector injector = Guice.createInjector(); MembersInjector<Object> membersInjector = injector.getMembersInjector(Object.class); membersInjector.injectMembers(new InjectionFailure()); } public void testInjectingMembersInjector() { InjectsMembersInjector injectsMembersInjector = Guice.createInjector(new AbstractModule() { protected void configure() { bind(C.class).toInstance(myFavouriteC); } }).getInstance(InjectsMembersInjector.class); A<C> a = new A<C>(); injectsMembersInjector.aMembersInjector.injectMembers(a); assertSame(myFavouriteC, a.t); assertSame(myFavouriteC, a.b.c); } public void testCannotBindMembersInjector() { try { Guice.createInjector(new AbstractModule() { protected void configure() { bind(MembersInjector.class).toProvider(Providers.<MembersInjector>of(null)); } }); fail(); } catch (CreationException expected) { assertContains(expected.getMessage(), "1) Binding to core guice framework type is not allowed: MembersInjector."); } try { Guice.createInjector(new AbstractModule() { protected void configure() { bind(new TypeLiteral<MembersInjector<A<C>>>() {}) .toProvider(Providers.<MembersInjector<A<C>>>of(null)); } }); fail(); } catch (CreationException expected) { assertContains(expected.getMessage(), "1) Binding to core guice framework type is not allowed: MembersInjector."); } } public void testInjectingMembersInjectorWithErrorsInDependencies() { try { Guice.createInjector().getInstance(InjectsBrokenMembersInjector.class); fail(); } catch (ConfigurationException expected) { assertContains(expected.getMessage(), "1) No implementation for " + Unimplemented.class.getName() + " was bound.", "while locating " + Unimplemented.class.getName(), "for field at " + A.class.getName() + ".t(MembersInjectorTest.java:", "while locating com.google.inject.MembersInjector<", "for field at " + InjectsBrokenMembersInjector.class.getName() + ".aMembersInjector(", "while locating " + InjectsBrokenMembersInjector.class.getName()); } } public void testLookupMembersInjectorBinding() { Injector injector = Guice.createInjector(new AbstractModule() { protected void configure() { bind(C.class).toInstance(myFavouriteC); } }); MembersInjector<A<C>> membersInjector = injector.getInstance(new Key<MembersInjector<A<C>>>() {}); A<C> a = new A<C>(); membersInjector.injectMembers(a); assertSame(myFavouriteC, a.t); assertSame(myFavouriteC, a.b.c); assertEquals("MembersInjector<java.lang.String>", injector.getInstance(new Key<MembersInjector<String>>() {}).toString()); } public void testGettingRawMembersInjector() { Injector injector = Guice.createInjector(); try { injector.getInstance(MembersInjector.class); fail(); } catch (ConfigurationException expected) { assertContains(expected.getMessage(), "Cannot inject a MembersInjector that has no type parameter"); } } public void testGettingAnnotatedMembersInjector() { Injector injector = Guice.createInjector(); try { injector.getInstance(new Key<MembersInjector<String>>(Names.named("foo")) {}); fail(); } catch (ConfigurationException expected) { assertContains(expected.getMessage(), "1) No implementation for com.google.inject.MembersInjector<java.lang.String> " + "annotated with @com.google.inject.name.Named(value=foo) was bound."); } } static class A<T> { @Inject B b; @Inject T t; @Inject void doNothing() {} } static class B { @Inject C c; @Inject void doNothing() {} } static class C {} static class NoInjectedMembers {} static class InjectionFailure { int failures = 0; @Inject void fail() { throw new ClassCastException("whoops, failure #" + (++failures)); } } static class InjectsMembersInjector { @Inject MembersInjector<A<C>> aMembersInjector; @Inject A<B> ab; } static class InjectsBrokenMembersInjector { @Inject MembersInjector<A<Unimplemented>> aMembersInjector; } static interface Unimplemented {} }