Home | History | Annotate | Download | only in fieldbinder
      1 /*
      2  * Copyright (C) 2014 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.testing.fieldbinder;
     18 
     19 import static com.google.inject.Asserts.assertContains;
     20 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     21 
     22 import com.google.inject.BindingAnnotation;
     23 import com.google.inject.ConfigurationException;
     24 import com.google.inject.CreationException;
     25 import com.google.inject.Guice;
     26 import com.google.inject.Inject;
     27 import com.google.inject.Injector;
     28 import com.google.inject.Key;
     29 import com.google.inject.Provider;
     30 import com.google.inject.ProvisionException;
     31 import com.google.inject.name.Named;
     32 import com.google.inject.name.Names;
     33 import com.google.inject.util.Providers;
     34 import java.lang.annotation.Retention;
     35 import java.util.Arrays;
     36 import java.util.List;
     37 import javax.inject.Qualifier;
     38 import junit.framework.TestCase;
     39 
     40 /** Unit tests for {@link BoundFieldModule}. */
     41 public class BoundFieldModuleTest extends TestCase {
     42   public void testBindingNothing() {
     43     Object instance = new Object() {};
     44 
     45     BoundFieldModule module = BoundFieldModule.of(instance);
     46     Guice.createInjector(module);
     47 
     48     // If we didn't throw an exception, we succeeded.
     49   }
     50 
     51   public void testBindingOnePrivate() {
     52     final Integer testValue = 1024;
     53     Object instance =
     54         new Object() {
     55           @Bind private Integer anInt = testValue;
     56         };
     57 
     58     BoundFieldModule module = BoundFieldModule.of(instance);
     59     Injector injector = Guice.createInjector(module);
     60 
     61     assertEquals(testValue, injector.getInstance(Integer.class));
     62   }
     63 
     64   public void testBindingOnePublic() {
     65     final Integer testValue = 1024;
     66     Object instance =
     67         new Object() {
     68           @Bind public Integer anInt = testValue;
     69         };
     70 
     71     BoundFieldModule module = BoundFieldModule.of(instance);
     72     Injector injector = Guice.createInjector(module);
     73 
     74     assertEquals(testValue, injector.getInstance(Integer.class));
     75   }
     76 
     77   private static class FieldBindableClass {
     78     @Bind Integer anInt;
     79 
     80     FieldBindableClass(Integer anInt) {
     81       this.anInt = anInt;
     82     }
     83   }
     84 
     85   private static class FieldBindableSubclass extends FieldBindableClass {
     86     FieldBindableSubclass(Integer anInt) {
     87       super(anInt);
     88     }
     89   }
     90 
     91   public void testSuperTypeBinding() {
     92     FieldBindableSubclass instance = new FieldBindableSubclass(1024);
     93 
     94     BoundFieldModule module = BoundFieldModule.of(instance);
     95     Injector injector = Guice.createInjector(module);
     96 
     97     assertEquals(instance.anInt, injector.getInstance(Integer.class));
     98   }
     99 
    100   public void testBindingTwo() {
    101     final Integer testValue = 1024;
    102     final String testString = "Hello World!";
    103     Object instance =
    104         new Object() {
    105           @Bind private Integer anInt = testValue;
    106           @Bind private String aString = testString;
    107         };
    108 
    109     BoundFieldModule module = BoundFieldModule.of(instance);
    110     Injector injector = Guice.createInjector(module);
    111 
    112     assertEquals(testValue, injector.getInstance(Integer.class));
    113     assertEquals(testString, injector.getInstance(String.class));
    114   }
    115 
    116   public void testBindingSuperType() {
    117     final Integer testValue = 1024;
    118     Object instance =
    119         new Object() {
    120           @Bind(to = Number.class)
    121           private Integer anInt = testValue;
    122         };
    123 
    124     BoundFieldModule module = BoundFieldModule.of(instance);
    125     Injector injector = Guice.createInjector(module);
    126 
    127     assertEquals(testValue, injector.getInstance(Number.class));
    128   }
    129 
    130   public void testBindingSuperTypeAccessSubType() {
    131     final Integer testValue = 1024;
    132     Object instance =
    133         new Object() {
    134           @Bind(to = Number.class)
    135           private Integer anInt = testValue;
    136         };
    137 
    138     BoundFieldModule module = BoundFieldModule.of(instance);
    139     Injector injector = Guice.createInjector(module);
    140 
    141     try {
    142       injector.getInstance(Integer.class);
    143       fail();
    144     } catch (ConfigurationException e) {
    145       assertContains(e.getMessage(), "Could not find a suitable constructor in java.lang.Integer");
    146     }
    147   }
    148 
    149   public void testBindingIncorrectTypeProviderFails() {
    150     final Integer testValue = 1024;
    151     Object instance =
    152         new Object() {
    153           @Bind(to = String.class)
    154           private Provider<Integer> anIntProvider =
    155               new Provider<Integer>() {
    156                 @Override
    157                 public Integer get() {
    158                   return testValue;
    159                 }
    160               };
    161         };
    162 
    163     BoundFieldModule module = BoundFieldModule.of(instance);
    164 
    165     try {
    166       Guice.createInjector(module);
    167       fail();
    168     } catch (CreationException e) {
    169       assertContains(
    170           e.getMessage(),
    171           "Requested binding type \"java.lang.String\" is not "
    172               + "assignable from field binding type \"java.lang.Integer\"");
    173     }
    174   }
    175 
    176   @BindingAnnotation
    177   @Retention(RUNTIME)
    178   private static @interface SomeBindingAnnotation {}
    179 
    180   public void testBindingWithBindingAnnotation() {
    181     final Integer testValue1 = 1024, testValue2 = 2048;
    182     Object instance =
    183         new Object() {
    184           @Bind private Integer anInt = testValue1;
    185 
    186           @Bind @SomeBindingAnnotation private Integer anotherInt = testValue2;
    187         };
    188 
    189     BoundFieldModule module = BoundFieldModule.of(instance);
    190     Injector injector = Guice.createInjector(module);
    191 
    192     assertEquals(testValue1, injector.getInstance(Integer.class));
    193     assertEquals(
    194         testValue2, injector.getInstance(Key.get(Integer.class, SomeBindingAnnotation.class)));
    195   }
    196 
    197   @Qualifier
    198   @Retention(RUNTIME)
    199   private static @interface SomeQualifier {}
    200 
    201   public void testBindingWithQualifier() {
    202     final Integer testValue1 = 1024, testValue2 = 2048;
    203     Object instance =
    204         new Object() {
    205           @Bind private Integer anInt = testValue1;
    206 
    207           @Bind @SomeQualifier private Integer anotherInt = testValue2;
    208         };
    209 
    210     BoundFieldModule module = BoundFieldModule.of(instance);
    211     Injector injector = Guice.createInjector(module);
    212 
    213     assertEquals(testValue1, injector.getInstance(Integer.class));
    214     assertEquals(testValue2, injector.getInstance(Key.get(Integer.class, SomeQualifier.class)));
    215   }
    216 
    217   public void testCanReuseBindingAnnotationsWithDifferentValues() {
    218     final Integer testValue1 = 1024, testValue2 = 2048;
    219     final String name1 = "foo", name2 = "bar";
    220     Object instance =
    221         new Object() {
    222           @Bind
    223           @Named(name1)
    224           private Integer anInt = testValue1;
    225 
    226           @Bind
    227           @Named(name2)
    228           private Integer anotherInt = testValue2;
    229         };
    230 
    231     BoundFieldModule module = BoundFieldModule.of(instance);
    232     Injector injector = Guice.createInjector(module);
    233 
    234     assertEquals(testValue1, injector.getInstance(Key.get(Integer.class, Names.named(name1))));
    235     assertEquals(testValue2, injector.getInstance(Key.get(Integer.class, Names.named(name2))));
    236   }
    237 
    238   public void testBindingWithValuedBindingAnnotation() {
    239     final Integer testValue1 = 1024, testValue2 = 2048;
    240     final String name = "foo";
    241     Object instance =
    242         new Object() {
    243           @Bind private Integer anInt = testValue1;
    244 
    245           @Bind
    246           @Named(name)
    247           private Integer anotherInt = testValue2;
    248         };
    249 
    250     BoundFieldModule module = BoundFieldModule.of(instance);
    251     Injector injector = Guice.createInjector(module);
    252 
    253     assertEquals(testValue1, injector.getInstance(Integer.class));
    254     assertEquals(testValue2, injector.getInstance(Key.get(Integer.class, Names.named(name))));
    255   }
    256 
    257   public void testBindingWithGenerics() {
    258     final List<Integer> testIntList = Arrays.asList(new Integer[] {1, 2, 3});
    259     final List<Boolean> testBoolList = Arrays.asList(new Boolean[] {true, true, false});
    260     Object instance =
    261         new Object() {
    262           @Bind private List<Integer> anIntList = testIntList;
    263           @Bind private List<Boolean> aBoolList = testBoolList;
    264         };
    265 
    266     BoundFieldModule module = BoundFieldModule.of(instance);
    267     Injector injector = Guice.createInjector(module);
    268 
    269     assertEquals(testIntList, injector.getInstance(new Key<List<Integer>>() {}));
    270     assertEquals(testBoolList, injector.getInstance(new Key<List<Boolean>>() {}));
    271   }
    272 
    273   public void testBoundValueDoesntChange() {
    274     Integer testValue = 1024;
    275     FieldBindableClass instance = new FieldBindableClass(testValue);
    276 
    277     BoundFieldModule module = BoundFieldModule.of(instance);
    278     Injector injector = Guice.createInjector(module);
    279 
    280     assertEquals(testValue, injector.getInstance(Integer.class));
    281     instance.anInt++;
    282     assertEquals(testValue, injector.getInstance(Integer.class));
    283   }
    284 
    285   public void testIncompatibleBindingType() {
    286     final Integer testInt = 1024;
    287     Object instance =
    288         new Object() {
    289           @Bind(to = String.class)
    290           private Integer anInt = testInt;
    291         };
    292 
    293     BoundFieldModule module = BoundFieldModule.of(instance);
    294 
    295     try {
    296       Guice.createInjector(module);
    297       fail();
    298     } catch (CreationException e) {
    299       assertContains(
    300           e.getMessage(),
    301           "Requested binding type \"java.lang.String\" is not assignable from field binding type "
    302               + "\"java.lang.Integer\"");
    303     }
    304   }
    305 
    306   public void testFailureOnMultipleBindingAnnotations() {
    307     final Integer testInt = 1024;
    308     Object instance =
    309         new Object() {
    310           @Bind
    311           @Named("a")
    312           @SomeBindingAnnotation
    313           private Integer anInt = testInt;
    314         };
    315 
    316     BoundFieldModule module = BoundFieldModule.of(instance);
    317 
    318     try {
    319       Guice.createInjector(module);
    320       fail();
    321     } catch (CreationException e) {
    322       assertContains(e.getMessage(), "More than one annotation is specified for this binding.");
    323     }
    324   }
    325 
    326   public void testBindingSuperTypeAndBindingAnnotation() {
    327     final Integer testValue = 1024;
    328     Object instance =
    329         new Object() {
    330           @Bind(to = Number.class)
    331           @Named("foo")
    332           private Integer anInt = testValue;
    333         };
    334 
    335     BoundFieldModule module = BoundFieldModule.of(instance);
    336     Injector injector = Guice.createInjector(module);
    337 
    338     assertEquals(testValue, injector.getInstance(Key.get(Number.class, Names.named("foo"))));
    339   }
    340 
    341   public void testBindingProvider() {
    342     final Integer testValue = 1024;
    343     Object instance =
    344         new Object() {
    345           @Bind
    346           private Provider<Integer> anInt =
    347               new Provider<Integer>() {
    348                 @Override
    349                 public Integer get() {
    350                   return testValue;
    351                 }
    352               };
    353         };
    354 
    355     BoundFieldModule module = BoundFieldModule.of(instance);
    356     Injector injector = Guice.createInjector(module);
    357 
    358     assertEquals(testValue, injector.getInstance(Integer.class));
    359   }
    360 
    361   public void testBindingJavaxProvider() {
    362     final Integer testValue = 1024;
    363     Object instance =
    364         new Object() {
    365           @Bind
    366           private javax.inject.Provider<Integer> anInt =
    367               new javax.inject.Provider<Integer>() {
    368                 @Override
    369                 public Integer get() {
    370                   return testValue;
    371                 }
    372               };
    373         };
    374 
    375     BoundFieldModule module = BoundFieldModule.of(instance);
    376     Injector injector = Guice.createInjector(module);
    377 
    378     assertEquals(testValue, injector.getInstance(Integer.class));
    379   }
    380 
    381   public void testBindingNonNullableNullField() {
    382     Object instance =
    383         new Object() {
    384           @Bind private Integer anInt = null;
    385         };
    386 
    387     BoundFieldModule module = BoundFieldModule.of(instance);
    388 
    389     try {
    390       Guice.createInjector(module);
    391       fail();
    392     } catch (CreationException e) {
    393       assertContains(
    394           e.getMessage(),
    395           "Binding to null values is only allowed for fields that are annotated @Nullable.");
    396     }
    397   }
    398 
    399   @Retention(RUNTIME)
    400   private @interface Nullable {}
    401 
    402   public void testBindingNullableNullField() {
    403     Object instance =
    404         new Object() {
    405           @Bind @Nullable private Integer anInt = null;
    406         };
    407 
    408     Injector injector = Guice.createInjector(BoundFieldModule.of(instance));
    409     assertNull(injector.getInstance(Integer.class));
    410   }
    411 
    412   public void testBindingNullProvider() {
    413     Object instance =
    414         new Object() {
    415           @Bind private Provider<Integer> anIntProvider = null;
    416         };
    417 
    418     BoundFieldModule module = BoundFieldModule.of(instance);
    419 
    420     try {
    421       Guice.createInjector(module);
    422       fail();
    423     } catch (CreationException e) {
    424       assertContains(
    425           e.getMessage(),
    426           "Binding to null is not allowed. Use Providers.of(null) if this is your intended "
    427               + "behavior.");
    428     }
    429   }
    430 
    431   public void testBindingNullableNullProvider() {
    432     Object instance =
    433         new Object() {
    434           @Bind @Nullable private Provider<Integer> anIntProvider = null;
    435         };
    436 
    437     BoundFieldModule module = BoundFieldModule.of(instance);
    438 
    439     try {
    440       Guice.createInjector(module);
    441       fail();
    442     } catch (CreationException e) {
    443       assertContains(
    444           e.getMessage(),
    445           "Binding to null is not allowed. Use Providers.of(null) if this is your intended "
    446               + "behavior.");
    447     }
    448   }
    449 
    450   private static class IntegerProvider implements Provider<Integer> {
    451     private final Integer value;
    452 
    453     IntegerProvider(Integer value) {
    454       this.value = value;
    455     }
    456 
    457     @Override
    458     public Integer get() {
    459       return value;
    460     }
    461   }
    462 
    463   public void testProviderSubclassesBindToTheProviderItself() {
    464     final IntegerProvider integerProvider = new IntegerProvider(1024);
    465     Object instance =
    466         new Object() {
    467           @Bind private IntegerProvider anIntProvider = integerProvider;
    468         };
    469 
    470     BoundFieldModule module = BoundFieldModule.of(instance);
    471     Injector injector = Guice.createInjector(module);
    472 
    473     assertEquals(integerProvider, injector.getInstance(IntegerProvider.class));
    474   }
    475 
    476   public void testProviderSubclassesDoNotBindParameterizedType() {
    477     final Integer testValue = 1024;
    478     Object instance =
    479         new Object() {
    480           @Bind private IntegerProvider anIntProvider = new IntegerProvider(testValue);
    481         };
    482 
    483     BoundFieldModule module = BoundFieldModule.of(instance);
    484     Injector injector = Guice.createInjector(module);
    485 
    486     try {
    487       injector.getInstance(Integer.class);
    488       fail();
    489     } catch (ConfigurationException e) {
    490       assertContains(e.getMessage(), "Could not find a suitable constructor in java.lang.Integer.");
    491     }
    492   }
    493 
    494   public void testNullableProviderSubclassesAllowNull() {
    495     Object instance =
    496         new Object() {
    497           @Bind @Nullable private IntegerProvider anIntProvider = null;
    498         };
    499 
    500     BoundFieldModule module = BoundFieldModule.of(instance);
    501     Injector injector = Guice.createInjector(module);
    502 
    503     assertNull(injector.getInstance(IntegerProvider.class));
    504   }
    505 
    506   private static class ParameterizedObject<T> {
    507     ParameterizedObject(T instance) {
    508       this.instance = instance;
    509     }
    510 
    511     @Bind private T instance;
    512   }
    513 
    514   public void testBindParameterizedTypeFails() {
    515     ParameterizedObject<Integer> instance = new ParameterizedObject<>(0);
    516 
    517     BoundFieldModule module = BoundFieldModule.of(instance);
    518 
    519     try {
    520       Guice.createInjector(module);
    521       fail();
    522     } catch (CreationException e) {
    523       assertContains(e.getMessage(), "T cannot be used as a key; It is not fully specified.");
    524     }
    525   }
    526 
    527   public void testBindSubclassOfParameterizedTypeSucceeds() {
    528     final Integer testValue = 1024;
    529     ParameterizedObject<Integer> instance = new ParameterizedObject<Integer>(testValue) {};
    530 
    531     BoundFieldModule module = BoundFieldModule.of(instance);
    532     Injector injector = Guice.createInjector(module);
    533 
    534     assertEquals(testValue, injector.getInstance(Integer.class));
    535   }
    536 
    537   public void testBindArray() {
    538     final Integer[] testArray = new Integer[] {1024, 2048};
    539     Object instance =
    540         new Object() {
    541           @Bind private Integer[] anIntArray = testArray;
    542         };
    543 
    544     BoundFieldModule module = BoundFieldModule.of(instance);
    545     Injector injector = Guice.createInjector(module);
    546 
    547     assertEquals(testArray, injector.getInstance(Integer[].class));
    548   }
    549 
    550   public void testRawProviderCannotBeBound() {
    551     final Integer testValue = 1024;
    552     Object instance =
    553         new Object() {
    554           @Bind
    555           private Provider anIntProvider =
    556               new Provider() {
    557                 @Override
    558                 public Object get() {
    559                   return testValue;
    560                 }
    561               };
    562         };
    563 
    564     BoundFieldModule module = BoundFieldModule.of(instance);
    565 
    566     try {
    567       Guice.createInjector(module);
    568       fail();
    569     } catch (CreationException e) {
    570       assertContains(
    571           e.getMessage(),
    572           "Non parameterized Provider fields must have an "
    573               + "explicit binding class via @Bind(to = Foo.class)");
    574     }
    575   }
    576 
    577   public void testExplicitlyBoundRawProviderCanBeBound() {
    578     final Integer testValue = 1024;
    579     Object instance =
    580         new Object() {
    581           @Bind(to = Integer.class)
    582           private Provider anIntProvider =
    583               new Provider() {
    584                 @Override
    585                 public Object get() {
    586                   return testValue;
    587                 }
    588               };
    589         };
    590 
    591     BoundFieldModule module = BoundFieldModule.of(instance);
    592     Injector injector = Guice.createInjector(module);
    593 
    594     assertEquals(testValue, injector.getInstance(Integer.class));
    595   }
    596 
    597   public void testRawProviderCanBindToIncorrectType() {
    598     final Integer testValue = 1024;
    599     Object instance =
    600         new Object() {
    601           @Bind(to = String.class)
    602           private Provider anIntProvider =
    603               new Provider() {
    604                 @Override
    605                 public Object get() {
    606                   return testValue;
    607                 }
    608               };
    609         };
    610 
    611     BoundFieldModule module = BoundFieldModule.of(instance);
    612     Injector injector = Guice.createInjector(module);
    613 
    614     assertEquals(testValue, injector.getInstance(String.class));
    615   }
    616 
    617   public void testMultipleErrorsAreAggregated() {
    618     Object instance =
    619         new Object() {
    620           @Bind private Provider aProvider;
    621 
    622           @Bind(to = String.class)
    623           private Integer anInt;
    624         };
    625 
    626     BoundFieldModule module = BoundFieldModule.of(instance);
    627     try {
    628       Guice.createInjector(module);
    629       fail();
    630     } catch (CreationException e) {
    631       assertEquals(2, e.getErrorMessages().size());
    632     }
    633   }
    634 
    635   public void testBindingProviderWithProviderSubclassValue() {
    636     final Integer testValue = 1024;
    637     Object instance =
    638         new Object() {
    639           @Bind private Provider<Integer> anIntProvider = new IntegerProvider(testValue);
    640         };
    641 
    642     BoundFieldModule module = BoundFieldModule.of(instance);
    643     Injector injector = Guice.createInjector(module);
    644 
    645     assertEquals(testValue, injector.getInstance(Integer.class));
    646   }
    647 
    648   public void testBoundFieldsCannotBeInjected() {
    649     Object instance =
    650         new Object() {
    651           @Bind @Inject Integer anInt = 0;
    652         };
    653 
    654     BoundFieldModule module = BoundFieldModule.of(instance);
    655 
    656     try {
    657       Guice.createInjector(module);
    658       fail();
    659     } catch (CreationException e) {
    660       assertContains(e.getMessage(), "Fields annotated with both @Bind and @Inject are illegal.");
    661     }
    662   }
    663 
    664   public void testIncrementingProvider() {
    665     final Integer testBaseValue = 1024;
    666     Object instance =
    667         new Object() {
    668           @Bind
    669           private Provider<Integer> anIntProvider =
    670               new Provider<Integer>() {
    671                 private int value = testBaseValue;
    672 
    673                 @Override
    674                 public Integer get() {
    675                   return value++;
    676                 }
    677               };
    678         };
    679 
    680     BoundFieldModule module = BoundFieldModule.of(instance);
    681     Injector injector = Guice.createInjector(module);
    682 
    683     assertEquals(testBaseValue, injector.getInstance(Integer.class));
    684     assertEquals((Integer) (testBaseValue + 1), injector.getInstance(Integer.class));
    685     assertEquals((Integer) (testBaseValue + 2), injector.getInstance(Integer.class));
    686   }
    687 
    688   public void testProviderDoesNotProvideDuringInjectorConstruction() {
    689     Object instance =
    690         new Object() {
    691           @Bind
    692           private Provider<Integer> myIntProvider =
    693               new Provider<Integer>() {
    694                 @Override
    695                 public Integer get() {
    696                   throw new UnsupportedOperationException();
    697                 }
    698               };
    699         };
    700 
    701     BoundFieldModule module = BoundFieldModule.of(instance);
    702     Guice.createInjector(module);
    703 
    704     // If we don't throw an exception, we succeeded.
    705   }
    706 
    707   private static class InvalidBindableClass {
    708     @Bind(to = String.class)
    709     Integer anInt;
    710   }
    711 
    712   public void testIncompatibleBindingTypeStackTraceHasUserFrame() {
    713     Object instance = new InvalidBindableClass();
    714 
    715     BoundFieldModule module = BoundFieldModule.of(instance);
    716 
    717     try {
    718       Guice.createInjector(module);
    719       fail();
    720     } catch (CreationException e) {
    721       assertContains(e.getMessage(), "at " + InvalidBindableClass.class.getName() + " field anInt");
    722     }
    723   }
    724 
    725   private static class InjectedNumberProvider implements Provider<Number> {
    726     @Inject Integer anInt;
    727 
    728     @Override
    729     public Number get() {
    730       return anInt;
    731     }
    732   }
    733 
    734   public void testBoundProvidersAreInjected() {
    735     final Integer testValue = 1024;
    736     Object instance =
    737         new Object() {
    738           @Bind private Integer anInt = testValue;
    739           @Bind private Provider<Number> aNumberProvider = new InjectedNumberProvider();
    740         };
    741 
    742     BoundFieldModule module = BoundFieldModule.of(instance);
    743     Injector injector = Guice.createInjector(module);
    744 
    745     assertEquals(testValue, injector.getInstance(Number.class));
    746   }
    747 
    748   public void testBoundInstancesAreInjected() {
    749     final Integer testValue = 1024;
    750     final InjectedNumberProvider testNumberProvider = new InjectedNumberProvider();
    751     Object instance =
    752         new Object() {
    753           @Bind private Integer anInt = testValue;
    754           @Bind private InjectedNumberProvider aNumberProvider = testNumberProvider;
    755         };
    756 
    757     BoundFieldModule module = BoundFieldModule.of(instance);
    758     Guice.createInjector(module);
    759 
    760     assertEquals(testValue, testNumberProvider.anInt);
    761   }
    762 
    763   private static class InvalidBindableSubclass extends InvalidBindableClass {}
    764 
    765   public void testClassIsPrintedInErrorsWhenCauseIsSuperclass() {
    766     Object instance = new InvalidBindableSubclass();
    767 
    768     BoundFieldModule module = BoundFieldModule.of(instance);
    769 
    770     try {
    771       Guice.createInjector(module);
    772       fail();
    773     } catch (CreationException e) {
    774       assertContains(
    775           e.getMessage(),
    776           "Requested binding type \"java.lang.String\" is not assignable from field binding type "
    777               + "\"java.lang.Integer\"");
    778     }
    779   }
    780 
    781   private static class FieldBindableSubclass2 extends FieldBindableClass {
    782     @Bind Number aNumber;
    783 
    784     FieldBindableSubclass2(Integer anInt, Number aNumber) {
    785       super(anInt);
    786       this.aNumber = aNumber;
    787     }
    788   }
    789 
    790   public void testFieldsAreBoundFromFullClassHierarchy() {
    791     final Integer testValue1 = 1024, testValue2 = 2048;
    792     FieldBindableSubclass2 instance = new FieldBindableSubclass2(testValue1, testValue2);
    793 
    794     BoundFieldModule module = BoundFieldModule.of(instance);
    795     Injector injector = Guice.createInjector(module);
    796 
    797     assertEquals(testValue1, injector.getInstance(Integer.class));
    798     assertEquals(testValue2, injector.getInstance(Number.class));
    799   }
    800 
    801   static final class LazyClass {
    802     @Bind(lazy = true)
    803     Integer foo = 1;
    804   }
    805 
    806   public void testFieldBound_lazy() {
    807     LazyClass asProvider = new LazyClass();
    808     Injector injector = Guice.createInjector(BoundFieldModule.of(asProvider));
    809     assertEquals(1, injector.getInstance(Integer.class).intValue());
    810     asProvider.foo++;
    811     assertEquals(2, injector.getInstance(Integer.class).intValue());
    812   }
    813 
    814   public void testNonNullableFieldBound_lazy_rejectNull() {
    815     LazyClass asProvider = new LazyClass();
    816     Injector injector = Guice.createInjector(BoundFieldModule.of(asProvider));
    817     assertEquals(1, injector.getInstance(Integer.class).intValue());
    818     asProvider.foo = null;
    819     try {
    820       injector.getInstance(Integer.class);
    821       fail();
    822     } catch (ProvisionException e) {
    823       assertContains(
    824           e.getMessage(),
    825           "Binding to null values is only allowed for fields that are annotated @Nullable.");
    826     }
    827   }
    828 
    829   static final class LazyClassNullable {
    830     @Bind(lazy = true)
    831     @Nullable
    832     Integer foo = 1;
    833   }
    834 
    835   public void testNullableFieldBound_lazy_allowNull() {
    836     LazyClassNullable asProvider = new LazyClassNullable();
    837     Injector injector = Guice.createInjector(BoundFieldModule.of(asProvider));
    838     assertEquals(1, injector.getInstance(Integer.class).intValue());
    839     asProvider.foo = null;
    840     assertNull(injector.getInstance(Integer.class));
    841   }
    842 
    843   static final class LazyProviderClass {
    844     @Bind(lazy = true)
    845     Provider<Integer> foo = Providers.of(null);
    846   }
    847 
    848   public void testFieldBoundAsProvider_lazy() {
    849     LazyProviderClass asProvider = new LazyProviderClass();
    850     Provider<Integer> provider =
    851         Guice.createInjector(BoundFieldModule.of(asProvider)).getProvider(Integer.class);
    852     assertNull(provider.get());
    853     asProvider.foo = Providers.of(1);
    854     assertEquals(1, provider.get().intValue());
    855     asProvider.foo =
    856         new Provider<Integer>() {
    857           @Override
    858           public Integer get() {
    859             throw new RuntimeException("boom");
    860           }
    861         };
    862     try {
    863       provider.get();
    864       fail();
    865     } catch (ProvisionException e) {
    866       assertContains(e.getMessage(), "boom");
    867     }
    868   }
    869 
    870   private static final class LazyNonTransparentProvider {
    871     @Bind(lazy = true)
    872     @Nullable
    873     private IntegerProvider anIntProvider = null;
    874   }
    875 
    876   public void testFieldBoundAsNonTransparentProvider_lazy() {
    877     LazyNonTransparentProvider instance = new LazyNonTransparentProvider();
    878     BoundFieldModule module = BoundFieldModule.of(instance);
    879     Injector injector = Guice.createInjector(module);
    880 
    881     assertNull(injector.getInstance(IntegerProvider.class));
    882     instance.anIntProvider = new IntegerProvider(3);
    883     assertEquals(3, injector.getInstance(IntegerProvider.class).get().intValue());
    884     try {
    885       injector.getInstance(Integer.class);
    886       fail();
    887     } catch (ConfigurationException expected) {
    888       // expected because we don't interpret IntegerProvider as a Provider<Integer>
    889     }
    890   }
    891 }
    892