Home | History | Annotate | Download | only in inject
      1 /*
      2 Copyright (C) 2007 Google Inc.
      3 
      4 Licensed under the Apache License, Version 2.0 (the "License");
      5 you may not use this file except in compliance with the License.
      6 You may obtain a copy of the License at
      7 
      8      http://www.apache.org/licenses/LICENSE-2.0
      9 
     10 Unless required by applicable law or agreed to in writing, software
     11 distributed under the License is distributed on an "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 See the License for the specific language governing permissions and
     14 limitations under the License.
     15 */
     16 
     17 package com.google.inject;
     18 
     19 import static com.google.inject.Asserts.assertContains;
     20 import static com.google.inject.Asserts.getDeclaringSourcePart;
     21 import static java.lang.annotation.ElementType.TYPE;
     22 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     23 
     24 import com.google.common.collect.ImmutableList;
     25 import com.google.common.collect.Iterables;
     26 import com.google.inject.matcher.Matchers;
     27 import com.google.inject.name.Names;
     28 import com.google.inject.spi.TypeConverter;
     29 
     30 import junit.framework.TestCase;
     31 
     32 import java.lang.annotation.Retention;
     33 import java.lang.annotation.Target;
     34 import java.util.List;
     35 
     36 /**
     37  * @author jessewilson (at) google.com (Jesse Wilson)
     38  */
     39 public class ParentInjectorTest extends TestCase {
     40 
     41   public void testParentAndChildCannotShareExplicitBindings() {
     42     Injector parent = Guice.createInjector(bindsA);
     43     try {
     44       parent.createChildInjector(bindsA);
     45       fail("Created the same explicit binding on both parent and child");
     46     } catch (CreationException e) {
     47       assertContains(e.getMessage(), "A binding to ", A.class.getName(), " was already configured",
     48           " at ", getClass().getName(), getDeclaringSourcePart(getClass()),
     49           " at ", getClass().getName(), getDeclaringSourcePart(getClass()));
     50     }
     51   }
     52 
     53   public void testParentJitBindingWontClobberChildBinding() {
     54     Injector parent = Guice.createInjector();
     55     parent.createChildInjector(bindsA);
     56     try {
     57       parent.getInstance(A.class);
     58       fail("Created a just-in-time binding on the parent that's the same as a child's binding");
     59     } catch (ConfigurationException e) {
     60       assertContains(e.getMessage(),
     61           "Unable to create binding for " + A.class.getName(),
     62           "It was already configured on one or more child injectors or private modules",
     63           "bound at " + bindsA.getClass().getName() + ".configure(",
     64           "If it was in a PrivateModule, did you forget to expose the binding?",
     65           "while locating " + A.class.getName());
     66     }
     67   }
     68 
     69   public void testChildCannotBindToAParentJitBinding() {
     70     Injector parent = Guice.createInjector();
     71     parent.getInstance(A.class);
     72     try {
     73       parent.createChildInjector(bindsA);
     74       fail();
     75     } catch(CreationException ce) {
     76       assertContains(Iterables.getOnlyElement(ce.getErrorMessages()).getMessage(),
     77           "A just-in-time binding to " + A.class.getName() + " was already configured on a parent injector.");
     78     }
     79   }
     80 
     81   public void testJustInTimeBindingsAreSharedWithParentIfPossible() {
     82     Injector parent = Guice.createInjector();
     83     Injector child = parent.createChildInjector();
     84     assertSame(child.getInstance(A.class), parent.getInstance(A.class));
     85 
     86     Injector anotherChild = parent.createChildInjector();
     87     assertSame(anotherChild.getInstance(A.class), parent.getInstance(A.class));
     88 
     89     Injector grandchild = child.createChildInjector();
     90     assertSame(grandchild.getInstance(A.class), parent.getInstance(A.class));
     91   }
     92 
     93   public void testBindingsInherited() {
     94     Injector parent = Guice.createInjector(bindsB);
     95     Injector child = parent.createChildInjector();
     96     assertSame(RealB.class, child.getInstance(B.class).getClass());
     97   }
     98 
     99   public void testGetParent() {
    100     Injector top = Guice.createInjector(bindsA);
    101     Injector middle = top.createChildInjector(bindsB);
    102     Injector bottom = middle.createChildInjector();
    103     assertSame(middle, bottom.getParent());
    104     assertSame(top, middle.getParent());
    105     assertNull(top.getParent());
    106   }
    107 
    108   public void testChildBindingsNotVisibleToParent() {
    109     Injector parent = Guice.createInjector();
    110     parent.createChildInjector(bindsB);
    111     try {
    112       parent.getBinding(B.class);
    113       fail();
    114     } catch (ConfigurationException expected) {
    115     }
    116   }
    117 
    118   public void testScopesInherited() {
    119     Injector parent = Guice.createInjector(new AbstractModule() {
    120       @Override protected void configure() {
    121         bindScope(MyScope.class, Scopes.SINGLETON);
    122       }
    123     });
    124     Injector child = parent.createChildInjector(new AbstractModule() {
    125       @Override protected void configure() {
    126         bind(A.class).in(MyScope.class);
    127       }
    128     });
    129     assertSame(child.getInstance(A.class), child.getInstance(A.class));
    130   }
    131 
    132   /*if[AOP]*/
    133   private final org.aopalliance.intercept.MethodInterceptor returnNullInterceptor
    134       = new org.aopalliance.intercept.MethodInterceptor() {
    135     public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation) {
    136       return null;
    137     }
    138   };
    139 
    140   public void testInterceptorsInherited() {
    141     Injector parent = Guice.createInjector(new AbstractModule() {
    142       @Override protected void configure() {
    143         super.bindInterceptor(Matchers.any(), Matchers.returns(Matchers.identicalTo(A.class)),
    144             returnNullInterceptor);
    145       }
    146     });
    147 
    148     Injector child = parent.createChildInjector(new AbstractModule() {
    149       @Override protected void configure() {
    150         bind(C.class);
    151       }
    152     });
    153 
    154     assertNull(child.getInstance(C.class).interceptedMethod());
    155   }
    156   /*end[AOP]*/
    157 
    158   public void testTypeConvertersInherited() {
    159     Injector parent = Guice.createInjector(bindListConverterModule);
    160     Injector child = parent.createChildInjector(bindStringNamedB);
    161 
    162     assertEquals(ImmutableList.of(), child.getInstance(Key.get(List.class, Names.named("B"))));
    163   }
    164 
    165   public void testTypeConvertersConflicting() {
    166     Injector parent = Guice.createInjector(bindListConverterModule);
    167     Injector child = parent.createChildInjector(bindListConverterModule, bindStringNamedB);
    168 
    169     try {
    170       child.getInstance(Key.get(List.class, Names.named("B")));
    171       fail();
    172     } catch (ConfigurationException expected) {
    173       Asserts.assertContains(expected.getMessage(), "Multiple converters can convert");
    174     }
    175   }
    176 
    177   public void testInjectorInjectionSpanningInjectors() {
    178     Injector parent = Guice.createInjector();
    179     Injector child = parent.createChildInjector(new AbstractModule() {
    180       @Override protected void configure() {
    181         bind(D.class);
    182       }
    183     });
    184 
    185     D d = child.getInstance(D.class);
    186     assertSame(d.injector, child);
    187 
    188     E e = child.getInstance(E.class);
    189     assertSame(e.injector, parent);
    190   }
    191 
    192   public void testSeveralLayersOfHierarchy() {
    193     Injector top = Guice.createInjector(bindsA);
    194     Injector left = top.createChildInjector();
    195     Injector leftLeft = left.createChildInjector(bindsD);
    196     Injector right = top.createChildInjector(bindsD);
    197 
    198     assertSame(leftLeft, leftLeft.getInstance(D.class).injector);
    199     assertSame(right, right.getInstance(D.class).injector);
    200     assertSame(top, leftLeft.getInstance(E.class).injector);
    201     assertSame(top.getInstance(A.class), leftLeft.getInstance(A.class));
    202 
    203     Injector leftRight = left.createChildInjector(bindsD);
    204     assertSame(leftRight, leftRight.getInstance(D.class).injector);
    205 
    206     try {
    207       top.getInstance(D.class);
    208       fail();
    209     } catch (ConfigurationException expected) {
    210     }
    211 
    212     try {
    213       left.getInstance(D.class);
    214       fail();
    215     } catch (ConfigurationException expected) {
    216     }
    217   }
    218 
    219   public void testScopeBoundInChildInjectorOnly() {
    220     Injector parent = Guice.createInjector();
    221     Injector child = parent.createChildInjector(new AbstractModule() {
    222       @Override protected void configure() {
    223         bindScope(MyScope.class, Scopes.SINGLETON);
    224       }
    225     });
    226 
    227     try {
    228       parent.getProvider(F.class);
    229       fail();
    230     } catch (ConfigurationException expected) {
    231       assertContains(expected.getMessage(),
    232           "No scope is bound to com.google.inject.ParentInjectorTest$MyScope.",
    233           "at " + F.class.getName() + ".class(ParentInjectorTest.java",
    234           "  while locating " + F.class.getName());
    235     }
    236 
    237     assertNotNull(child.getProvider(F.class).get());
    238   }
    239 
    240   public void testErrorInParentButOkayInChild() {
    241     Injector parent = Guice.createInjector();
    242     Injector childInjector = parent.createChildInjector(new AbstractModule() {
    243       @Override protected void configure() {
    244         bindScope(MyScope.class, Scopes.SINGLETON);
    245         bind(Object.class).to(F.class);
    246       }
    247     });
    248     Object one = childInjector.getInstance(Object.class);
    249     Object two = childInjector.getInstance(Object.class);
    250     assertSame(one, two);
    251   }
    252 
    253   public void testErrorInParentAndChild() {
    254     Injector parent = Guice.createInjector();
    255     Injector childInjector = parent.createChildInjector();
    256 
    257     try {
    258       childInjector.getInstance(G.class);
    259       fail();
    260     } catch(ConfigurationException expected) {
    261       assertContains(expected.getMessage(), "No scope is bound to " + MyScope.class.getName(),
    262           "at " + F.class.getName() + ".class(ParentInjectorTest.java:",
    263           "  while locating " + G.class.getName());
    264     }
    265   }
    266 
    267   @Singleton
    268   static class A {}
    269 
    270   private final Module bindsA = new AbstractModule() {
    271     @Override protected void configure() {
    272       bind(A.class).toInstance(new A());
    273     }
    274   };
    275 
    276   interface B {}
    277   static class RealB implements B {}
    278 
    279   private final Module bindsB = new AbstractModule() {
    280     @Override protected void configure() {
    281       bind(B.class).to(RealB.class);
    282     }
    283   };
    284 
    285   @Target(TYPE) @Retention(RUNTIME) @ScopeAnnotation
    286   public @interface MyScope {}
    287 
    288   private final TypeConverter listConverter = new TypeConverter() {
    289     public Object convert(String value, TypeLiteral<?> toType) {
    290       return ImmutableList.of();
    291     }
    292   };
    293 
    294   private final Module bindListConverterModule = new AbstractModule() {
    295     @Override protected void configure() {
    296       convertToTypes(Matchers.any(), listConverter);
    297     }
    298   };
    299 
    300   private final Module bindStringNamedB = new AbstractModule() {
    301     @Override protected void configure() {
    302       bind(String.class).annotatedWith(Names.named("B")).toInstance("buzz");
    303     }
    304   };
    305 
    306   public static class C {
    307     public A interceptedMethod() {
    308       return new A();
    309     }
    310   }
    311 
    312   static class D {
    313     @Inject Injector injector;
    314   }
    315 
    316   static class E {
    317     @Inject Injector injector;
    318   }
    319 
    320   private final Module bindsD = new AbstractModule() {
    321     @Override protected void configure() {
    322       bind(D.class);
    323     }
    324   };
    325 
    326   @MyScope
    327   static class F implements G {}
    328 
    329   @ImplementedBy(F.class)
    330   interface G {}
    331 }
    332