Home | History | Annotate | Download | only in inject
      1 package com.google.inject;
      2 
      3 import static com.google.inject.Asserts.assertContains;
      4 import static com.google.inject.Asserts.getDeclaringSourcePart;
      5 
      6 import junit.framework.TestCase;
      7 
      8 import java.lang.annotation.Documented;
      9 import java.lang.annotation.ElementType;
     10 import java.lang.annotation.Retention;
     11 import java.lang.annotation.RetentionPolicy;
     12 import java.lang.annotation.Target;
     13 
     14 /**
     15  * @author jessewilson (at) google.com (Jesse Wilson)
     16  */
     17 public class NullableInjectionPointTest extends TestCase {
     18 
     19   public void testInjectNullIntoNotNullableConstructor() {
     20     try {
     21       createInjector().getInstance(FooConstructor.class);
     22       fail("Injecting null should fail with an error");
     23     }
     24     catch (ProvisionException expected) {
     25       assertContains(expected.getMessage(),
     26           "null returned by binding at " + getClass().getName(),
     27           "parameter 0 of " + FooConstructor.class.getName() + ".<init>() is not @Nullable");
     28     }
     29   }
     30 
     31   public void testInjectNullIntoNotNullableMethod() {
     32     try {
     33       createInjector().getInstance(FooMethod.class);
     34       fail("Injecting null should fail with an error");
     35     }
     36     catch (ProvisionException expected) {
     37       assertContains(expected.getMessage(),
     38           "null returned by binding at " + getClass().getName(),
     39           "parameter 0 of " + FooMethod.class.getName() + ".setFoo() is not @Nullable");
     40     }
     41   }
     42 
     43   public void testInjectNullIntoNotNullableField() {
     44     try {
     45       createInjector().getInstance(FooField.class);
     46       fail("Injecting null should fail with an error");
     47     }
     48     catch (ProvisionException expected) {
     49       assertContains(expected.getMessage(),
     50           "null returned by binding at " + getClass().getName(),
     51           " but " + FooField.class.getName() + ".foo is not @Nullable");
     52     }
     53   }
     54 
     55   /**
     56    * Provider.getInstance() is allowed to return null via direct calls to
     57    * getInstance().
     58    */
     59   public void testGetInstanceOfNull() {
     60     assertNull(createInjector().getInstance(Foo.class));
     61   }
     62 
     63   public void testInjectNullIntoNullableConstructor() {
     64     NullableFooConstructor nfc
     65         = createInjector().getInstance(NullableFooConstructor.class);
     66     assertNull(nfc.foo);
     67   }
     68 
     69   public void testInjectNullIntoNullableMethod() {
     70     NullableFooMethod nfm
     71         = createInjector().getInstance(NullableFooMethod.class);
     72     assertNull(nfm.foo);
     73   }
     74 
     75   public void testInjectNullIntoNullableField() {
     76     NullableFooField nff
     77         = createInjector().getInstance(NullableFooField.class);
     78     assertNull(nff.foo);
     79   }
     80 
     81   public void testInjectNullIntoCustomNullableConstructor() {
     82     CustomNullableFooConstructor nfc
     83         = createInjector().getInstance(CustomNullableFooConstructor.class);
     84     assertNull(nfc.foo);
     85   }
     86 
     87   public void testInjectNullIntoCustomNullableMethod() {
     88     CustomNullableFooMethod nfm
     89         = createInjector().getInstance(CustomNullableFooMethod.class);
     90     assertNull(nfm.foo);
     91   }
     92 
     93   public void testInjectNullIntoCustomNullableField() {
     94     CustomNullableFooField nff
     95         = createInjector().getInstance(CustomNullableFooField.class);
     96     assertNull(nff.foo);
     97   }
     98 
     99   private Injector createInjector() {
    100     return Guice.createInjector(
    101         new AbstractModule() {
    102           protected void configure() {
    103             bind(Foo.class).toProvider(new Provider<Foo>() {
    104               public Foo get() {
    105                 return null;
    106               }
    107             });
    108           }
    109         });
    110   }
    111 
    112   /**
    113    * We haven't decided on what the desired behaviour of this test should be...
    114    */
    115   public void testBindNullToInstance() {
    116     try {
    117       Guice.createInjector(new AbstractModule() {
    118         protected void configure() {
    119           bind(Foo.class).toInstance(null);
    120         }
    121       });
    122       fail();
    123     } catch (CreationException expected) {
    124       assertContains(expected.getMessage(),
    125           "Binding to null instances is not allowed.",
    126           "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
    127     }
    128   }
    129 
    130   public void testBindNullToProvider() {
    131     Injector injector = Guice.createInjector(new AbstractModule() {
    132       protected void configure() {
    133         bind(Foo.class).toProvider(new Provider<Foo>() {
    134           public Foo get() {
    135             return null;
    136           }
    137         });
    138       }
    139     });
    140     assertNull(injector.getInstance(NullableFooField.class).foo);
    141     assertNull(injector.getInstance(CustomNullableFooField.class).foo);
    142 
    143     try {
    144       injector.getInstance(FooField.class);
    145     }
    146     catch(ProvisionException expected) {
    147       assertContains(expected.getMessage(), "null returned by binding at");
    148     }
    149   }
    150 
    151   public void testBindScopedNull() {
    152     Injector injector = Guice.createInjector(new AbstractModule() {
    153       protected void configure() {
    154         bind(Foo.class).toProvider(new Provider<Foo>() {
    155           public Foo get() {
    156             return null;
    157           }
    158         }).in(Scopes.SINGLETON);
    159       }
    160     });
    161     assertNull(injector.getInstance(NullableFooField.class).foo);
    162     assertNull(injector.getInstance(CustomNullableFooField.class).foo);
    163 
    164     try {
    165       injector.getInstance(FooField.class);
    166     }
    167     catch(ProvisionException expected) {
    168       assertContains(expected.getMessage(), "null returned by binding at");
    169     }
    170   }
    171 
    172   public void testBindNullAsEagerSingleton() {
    173     Injector injector = Guice.createInjector(new AbstractModule() {
    174       protected void configure() {
    175         bind(Foo.class).toProvider(new Provider<Foo>() {
    176           public Foo get() {
    177             return null;
    178           }
    179         }).asEagerSingleton();
    180       }
    181     });
    182     assertNull(injector.getInstance(NullableFooField.class).foo);
    183     assertNull(injector.getInstance(CustomNullableFooField.class).foo);
    184 
    185     try {
    186       injector.getInstance(FooField.class);
    187       fail();
    188     } catch(ProvisionException expected) {
    189       assertContains(expected.getMessage(), "null returned by binding "
    190           + "at com.google.inject.NullableInjectionPointTest");
    191     }
    192   }
    193 
    194   static class Foo { }
    195 
    196   static class FooConstructor {
    197     @Inject FooConstructor(Foo foo) { }
    198   }
    199   static class FooField {
    200     @Inject Foo foo;
    201   }
    202   static class FooMethod {
    203     @Inject
    204     void setFoo(Foo foo) { }
    205   }
    206 
    207   static class NullableFooConstructor {
    208     Foo foo;
    209     @Inject NullableFooConstructor(@Nullable Foo foo) {
    210       this.foo = foo;
    211     }
    212   }
    213   static class NullableFooField {
    214     @Inject @Nullable Foo foo;
    215   }
    216   static class NullableFooMethod {
    217     Foo foo;
    218     @Inject void setFoo(@Nullable Foo foo) {
    219       this.foo = foo;
    220     }
    221   }
    222 
    223   static class CustomNullableFooConstructor {
    224     Foo foo;
    225     @Inject CustomNullableFooConstructor(@Namespace.Nullable Foo foo) {
    226       this.foo = foo;
    227     }
    228   }
    229 
    230   static class CustomNullableFooField {
    231     @Inject @Namespace.Nullable Foo foo;
    232   }
    233   static class CustomNullableFooMethod {
    234     Foo foo;
    235     @Inject void setFoo(@Namespace.Nullable Foo foo) {
    236       this.foo = foo;
    237     }
    238   }
    239 
    240   @Documented
    241   @Retention(RetentionPolicy.RUNTIME)
    242   @Target({ElementType.PARAMETER, ElementType.FIELD})
    243   @interface Nullable { }
    244 
    245   static interface Namespace {
    246     @Documented
    247     @Retention(RetentionPolicy.RUNTIME)
    248     @Target({ElementType.PARAMETER, ElementType.FIELD})
    249     @interface Nullable { }
    250   }
    251 }
    252