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@google.com (Jesse Wilson) 16 */ 17 public class NullableInjectionPointTest extends TestCase { 18 testInjectNullIntoNotNullableConstructor()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 testInjectNullIntoNotNullableMethod()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 testInjectNullIntoNotNullableField()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 */ testGetInstanceOfNull()59 public void testGetInstanceOfNull() { 60 assertNull(createInjector().getInstance(Foo.class)); 61 } 62 testInjectNullIntoNullableConstructor()63 public void testInjectNullIntoNullableConstructor() { 64 NullableFooConstructor nfc 65 = createInjector().getInstance(NullableFooConstructor.class); 66 assertNull(nfc.foo); 67 } 68 testInjectNullIntoNullableMethod()69 public void testInjectNullIntoNullableMethod() { 70 NullableFooMethod nfm 71 = createInjector().getInstance(NullableFooMethod.class); 72 assertNull(nfm.foo); 73 } 74 testInjectNullIntoNullableField()75 public void testInjectNullIntoNullableField() { 76 NullableFooField nff 77 = createInjector().getInstance(NullableFooField.class); 78 assertNull(nff.foo); 79 } 80 testInjectNullIntoCustomNullableConstructor()81 public void testInjectNullIntoCustomNullableConstructor() { 82 CustomNullableFooConstructor nfc 83 = createInjector().getInstance(CustomNullableFooConstructor.class); 84 assertNull(nfc.foo); 85 } 86 testInjectNullIntoCustomNullableMethod()87 public void testInjectNullIntoCustomNullableMethod() { 88 CustomNullableFooMethod nfm 89 = createInjector().getInstance(CustomNullableFooMethod.class); 90 assertNull(nfm.foo); 91 } 92 testInjectNullIntoCustomNullableField()93 public void testInjectNullIntoCustomNullableField() { 94 CustomNullableFooField nff 95 = createInjector().getInstance(CustomNullableFooField.class); 96 assertNull(nff.foo); 97 } 98 createInjector()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