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