| OLD | NEW |
| (Empty) | |
| 1 @Injectables(const [ |
| 2 ClassOne, |
| 3 CircularA, |
| 4 CircularB, |
| 5 MultipleConstructors, |
| 6 NumDependency, |
| 7 IntDependency, |
| 8 DoubleDependency, |
| 9 BoolDependency, |
| 10 StringDependency |
| 11 ]) |
| 12 library di.tests; |
| 13 |
| 14 import 'fixed-unittest.dart'; |
| 15 import 'package:di/di.dart'; |
| 16 import 'package:di/dynamic_injector.dart'; |
| 17 import 'package:di/static_injector.dart'; |
| 18 import 'package:di/annotations.dart'; |
| 19 |
| 20 // Generated file. Run ../test_tf_gen.sh. |
| 21 import 'type_factories_gen.dart' as type_factories_gen; |
| 22 |
| 23 /** |
| 24 * Annotation used to mark classes for which static type factory must be |
| 25 * generated. For testing purposes not all classes are marked with this |
| 26 * annotation, some classes are included in @Injectables at the top. |
| 27 */ |
| 28 class Injectable { |
| 29 const Injectable(); |
| 30 } |
| 31 |
| 32 // just some classes for testing |
| 33 @Injectable() |
| 34 class Engine { |
| 35 String id = 'v8-id'; |
| 36 } |
| 37 |
| 38 @Injectable() |
| 39 class MockEngine implements Engine { |
| 40 String id = 'mock-id'; |
| 41 } |
| 42 |
| 43 @Injectable() |
| 44 class MockEngine2 implements Engine { |
| 45 String id = 'mock-id-2'; |
| 46 } |
| 47 |
| 48 @Injectable() |
| 49 class Car { |
| 50 Engine engine; |
| 51 Injector injector; |
| 52 |
| 53 Car(this.engine, this.injector); |
| 54 } |
| 55 |
| 56 class NumDependency { |
| 57 NumDependency(num value) {} |
| 58 } |
| 59 |
| 60 class IntDependency { |
| 61 IntDependency(int value) {} |
| 62 } |
| 63 |
| 64 class DoubleDependency { |
| 65 DoubleDependency(double value) {} |
| 66 } |
| 67 |
| 68 class StringDependency { |
| 69 StringDependency(String value) {} |
| 70 } |
| 71 |
| 72 class BoolDependency { |
| 73 BoolDependency(bool value) {} |
| 74 } |
| 75 |
| 76 |
| 77 class CircularA { |
| 78 CircularA(CircularB b) {} |
| 79 } |
| 80 |
| 81 class CircularB { |
| 82 CircularB(CircularA a) {} |
| 83 } |
| 84 |
| 85 typedef int CompareInt(int a, int b); |
| 86 |
| 87 int compareIntAsc(int a, int b) => b.compareTo(a); |
| 88 |
| 89 class WithTypeDefDependency { |
| 90 CompareInt compare; |
| 91 |
| 92 WithTypeDefDependency(CompareInt c) { |
| 93 compare = c; |
| 94 } |
| 95 } |
| 96 |
| 97 class MultipleConstructors { |
| 98 String instantiatedVia; |
| 99 MultipleConstructors() : instantiatedVia = 'default'; |
| 100 MultipleConstructors.named() : instantiatedVia = 'named'; |
| 101 } |
| 102 |
| 103 class InterfaceOne { |
| 104 } |
| 105 |
| 106 class ClassOne implements InterfaceOne { |
| 107 ClassOne(Log log) { |
| 108 log.add('ClassOne'); |
| 109 } |
| 110 } |
| 111 |
| 112 @Injectable() |
| 113 class Log { |
| 114 var log = []; |
| 115 |
| 116 add(String message) => log.add(message); |
| 117 } |
| 118 |
| 119 class EmulatedMockEngineFactory { |
| 120 call(Injector i) => new MockEngine(); |
| 121 } |
| 122 |
| 123 void main() { |
| 124 createInjectorSpec('DynamicInjector', |
| 125 (modules, [name]) => new DynamicInjector(modules: modules, name: name)); |
| 126 |
| 127 // Initialize generated type factories. |
| 128 type_factories_gen.main(); |
| 129 |
| 130 createInjectorSpec('StaticInjector', |
| 131 (modules, [name]) => new StaticInjector(modules: modules, name: name, |
| 132 typeFactories: type_factories_gen.typeFactories)); |
| 133 } |
| 134 |
| 135 typedef Injector InjectorFactory(List<Module> modules, [String name]); |
| 136 |
| 137 createInjectorSpec(String injectorName, InjectorFactory injectorFactory) { |
| 138 |
| 139 describe(injectorName, () { |
| 140 |
| 141 it('should instantiate a type', () { |
| 142 var injector = injectorFactory([new Module()..type(Engine)]); |
| 143 var instance = injector.get(Engine); |
| 144 |
| 145 expect(instance, instanceOf(Engine)); |
| 146 expect(instance.id, toEqual('v8-id')); |
| 147 }); |
| 148 |
| 149 it('should fail if no binding is found', () { |
| 150 var injector = injectorFactory([]); |
| 151 expect(() { |
| 152 injector.get(Engine); |
| 153 }, toThrow(NoProviderError, 'No provider found for Engine! ' |
| 154 '(resolving Engine)')); |
| 155 }); |
| 156 |
| 157 |
| 158 it('should resolve basic dependencies', () { |
| 159 var injector = injectorFactory([new Module()..type(Car)..type(Engine)]); |
| 160 var instance = injector.get(Car); |
| 161 |
| 162 expect(instance, instanceOf(Car)); |
| 163 expect(instance.engine.id, toEqual('v8-id')); |
| 164 }); |
| 165 |
| 166 |
| 167 it('should allow modules and overriding providers', () { |
| 168 var module = new Module()..type(Engine, implementedBy: MockEngine); |
| 169 |
| 170 // injector is immutable |
| 171 // you can't load more modules once it's instantiated |
| 172 // (you can create a child injector) |
| 173 var injector = injectorFactory([module]); |
| 174 var instance = injector.get(Engine); |
| 175 |
| 176 expect(instance.id, toEqual('mock-id')); |
| 177 }); |
| 178 |
| 179 |
| 180 it('should only create a single instance', () { |
| 181 var injector = injectorFactory([new Module()..type(Engine)]); |
| 182 var first = injector.get(Engine); |
| 183 var second = injector.get(Engine); |
| 184 |
| 185 expect(first, toBe(second)); |
| 186 }); |
| 187 |
| 188 |
| 189 it('should allow providing values', () { |
| 190 var module = new Module() |
| 191 ..value(Engine, 'str value') |
| 192 ..value(Car, 123); |
| 193 |
| 194 var injector = injectorFactory([module]); |
| 195 var abcInstance = injector.get(Engine); |
| 196 var complexInstance = injector.get(Car); |
| 197 |
| 198 expect(abcInstance, toEqual('str value')); |
| 199 expect(complexInstance, toEqual(123)); |
| 200 }); |
| 201 |
| 202 |
| 203 it('should allow providing factory functions', () { |
| 204 var module = new Module()..factory(Engine, (Injector injector) { |
| 205 return 'factory-product'; |
| 206 }); |
| 207 |
| 208 var injector = injectorFactory([module]); |
| 209 var instance = injector.get(Engine); |
| 210 |
| 211 expect(instance, toEqual('factory-product')); |
| 212 }); |
| 213 |
| 214 |
| 215 it('should allow providing with emulated factory functions', () { |
| 216 var module = new Module(); |
| 217 module.factory(Engine, new EmulatedMockEngineFactory()); |
| 218 |
| 219 var injector = injectorFactory([module]); |
| 220 var instance = injector.get(Engine); |
| 221 |
| 222 expect(instance, new isInstanceOf<MockEngine>()); |
| 223 }); |
| 224 |
| 225 |
| 226 it('should inject injector into factory function', () { |
| 227 var module = new Module() |
| 228 ..type(Engine) |
| 229 ..factory(Car, (Injector injector) { |
| 230 return new Car(injector.get(Engine), injector); |
| 231 }); |
| 232 |
| 233 var injector = injectorFactory([module]); |
| 234 var instance = injector.get(Car); |
| 235 |
| 236 expect(instance, instanceOf(Car)); |
| 237 expect(instance.engine.id, toEqual('v8-id')); |
| 238 }); |
| 239 |
| 240 |
| 241 it('should throw an exception when injecting a primitive type', () { |
| 242 var injector = injectorFactory([ |
| 243 new Module() |
| 244 ..type(NumDependency) |
| 245 ..type(IntDependency) |
| 246 ..type(DoubleDependency) |
| 247 ..type(BoolDependency) |
| 248 ..type(StringDependency) |
| 249 ]); |
| 250 |
| 251 expect(() { |
| 252 injector.get(NumDependency); |
| 253 }, toThrow(NoProviderError, 'Cannot inject a primitive type of num! ' |
| 254 '(resolving NumDependency -> num)')); |
| 255 |
| 256 expect(() { |
| 257 injector.get(IntDependency); |
| 258 }, toThrow(NoProviderError, 'Cannot inject a primitive type of int! ' |
| 259 '(resolving IntDependency -> int)')); |
| 260 |
| 261 expect(() { |
| 262 injector.get(DoubleDependency); |
| 263 }, toThrow(NoProviderError, 'Cannot inject a primitive type of double! ' |
| 264 '(resolving DoubleDependency -> double)')); |
| 265 |
| 266 expect(() { |
| 267 injector.get(BoolDependency); |
| 268 }, toThrow(NoProviderError, 'Cannot inject a primitive type of bool! ' |
| 269 '(resolving BoolDependency -> bool)')); |
| 270 |
| 271 expect(() { |
| 272 injector.get(StringDependency); |
| 273 }, toThrow(NoProviderError, 'Cannot inject a primitive type of String! ' |
| 274 '(resolving StringDependency -> String)')); |
| 275 }); |
| 276 |
| 277 |
| 278 it('should throw an exception when circular dependency', () { |
| 279 var injector = injectorFactory([new Module()..type(CircularA)..type(Circul
arB)]); |
| 280 |
| 281 expect(() { |
| 282 injector.get(CircularA); |
| 283 }, toThrow(CircularDependencyError, 'Cannot resolve a circular dependency!
' |
| 284 '(resolving CircularA -> ' |
| 285 'CircularB -> CircularA)')); |
| 286 }); |
| 287 |
| 288 |
| 289 it('should provide the injector as Injector', () { |
| 290 var injector = injectorFactory([]); |
| 291 |
| 292 expect(injector.get(Injector), toBe(injector)); |
| 293 }); |
| 294 |
| 295 |
| 296 // Typedef injection is not supported in dart2js: http://dartbug.com/11612 |
| 297 xit('should inject a typedef', () { |
| 298 var module = new Module()..value(CompareInt, compareIntAsc); |
| 299 |
| 300 var injector = injectorFactory([module]); |
| 301 var compare = injector.get(CompareInt); |
| 302 |
| 303 expect(compare(1, 2), toBe(1)); |
| 304 expect(compare(5, 2), toBe(-1)); |
| 305 }); |
| 306 |
| 307 |
| 308 // Typedef injection is not supported in dart2js: http://dartbug.com/11612 |
| 309 xit('should throw an exception when injecting typedef without providing it',
() { |
| 310 var injector = injectorFactory([new Module()..type(WithTypeDefDependency)]
); |
| 311 |
| 312 expect(() { |
| 313 injector.get(WithTypeDefDependency); |
| 314 }, toThrow(NoProviderError, 'No provider found for CompareInt! ' |
| 315 '(resolving WithTypeDefDependency -> CompareInt)')); |
| 316 }); |
| 317 |
| 318 |
| 319 it('should instantiate via the default/unnamed constructor', () { |
| 320 var injector = injectorFactory([new Module()..type(MultipleConstructors)])
; |
| 321 MultipleConstructors instance = injector.get(MultipleConstructors); |
| 322 expect(instance.instantiatedVia, 'default'); |
| 323 }); |
| 324 |
| 325 // CHILD INJECTORS |
| 326 it('should inject from child', () { |
| 327 var module = new Module()..type(Engine, implementedBy: MockEngine); |
| 328 |
| 329 var parent = injectorFactory([new Module()..type(Engine)]); |
| 330 var child = parent.createChild([module]); |
| 331 |
| 332 var abcFromParent = parent.get(Engine); |
| 333 var abcFromChild = child.get(Engine); |
| 334 |
| 335 expect(abcFromParent.id, toEqual('v8-id')); |
| 336 expect(abcFromChild.id, toEqual('mock-id')); |
| 337 }); |
| 338 |
| 339 |
| 340 it('should enumerate across children', () { |
| 341 var parent = injectorFactory([new Module()..type(Engine)]); |
| 342 var child = parent.createChild([new Module()..type(MockEngine)]); |
| 343 |
| 344 expect(parent.types, unorderedEquals(new Set.from([Engine, Injector]))); |
| 345 expect(child.types, unorderedEquals(new Set.from([Engine, MockEngine, Inje
ctor]))); |
| 346 }); |
| 347 |
| 348 |
| 349 it('should inject instance from parent if not provided in child', () { |
| 350 var module = new Module()..type(Car); |
| 351 |
| 352 var parent = injectorFactory([new Module()..type(Car)..type(Engine)]); |
| 353 var child = parent.createChild([module]); |
| 354 |
| 355 var complexFromParent = parent.get(Car); |
| 356 var complexFromChild = child.get(Car); |
| 357 var abcFromParent = parent.get(Engine); |
| 358 var abcFromChild = child.get(Engine); |
| 359 |
| 360 expect(complexFromChild, not(toBe(complexFromParent))); |
| 361 expect(abcFromChild, toBe(abcFromParent)); |
| 362 }); |
| 363 |
| 364 |
| 365 it('should inject instance from parent but never use dependency from child',
() { |
| 366 var module = new Module()..type(Engine, implementedBy: MockEngine); |
| 367 |
| 368 var parent = injectorFactory([new Module()..type(Car)..type(Engine)]); |
| 369 var child = parent.createChild([module]); |
| 370 |
| 371 var complexFromParent = parent.get(Car); |
| 372 var complexFromChild = child.get(Car); |
| 373 var abcFromParent = parent.get(Engine); |
| 374 var abcFromChild = child.get(Engine); |
| 375 |
| 376 expect(complexFromChild, toBe(complexFromParent)); |
| 377 expect(complexFromChild.engine, toBe(abcFromParent)); |
| 378 expect(complexFromChild.engine, not(toBe(abcFromChild))); |
| 379 }); |
| 380 |
| 381 |
| 382 it('should force new instance in child even if already instantiated in paren
t', () { |
| 383 var parent = injectorFactory([new Module()..type(Engine)]); |
| 384 var abcAlreadyInParent = parent.get(Engine); |
| 385 |
| 386 var child = parent.createChild([], forceNewInstances: [Engine]); |
| 387 var abcFromChild = child.get(Engine); |
| 388 |
| 389 expect(abcFromChild, not(toBe(abcAlreadyInParent))); |
| 390 }); |
| 391 |
| 392 |
| 393 it('should force new instance in child using provider from grand parent', ()
{ |
| 394 var module = new Module()..type(Engine, implementedBy: MockEngine); |
| 395 |
| 396 var grandParent = injectorFactory([module]); |
| 397 var parent = grandParent.createChild([]); |
| 398 var child = parent.createChild([], forceNewInstances: [Engine]); |
| 399 |
| 400 var abcFromGrandParent = grandParent.get(Engine); |
| 401 var abcFromChild = child.get(Engine); |
| 402 |
| 403 expect(abcFromChild.id, toEqual(('mock-id'))); |
| 404 expect(abcFromChild, not(toBe(abcFromGrandParent))); |
| 405 }); |
| 406 |
| 407 |
| 408 it('should provide child injector as Injector', () { |
| 409 var injector = injectorFactory([]); |
| 410 var child = injector.createChild([]); |
| 411 |
| 412 expect(child.get(Injector), toBe(child)); |
| 413 }); |
| 414 |
| 415 |
| 416 it('should set the injector name', () { |
| 417 var injector = injectorFactory([], 'foo'); |
| 418 expect(injector.name, 'foo'); |
| 419 }); |
| 420 |
| 421 |
| 422 it('should set the child injector name', () { |
| 423 var injector = injectorFactory([], 'foo'); |
| 424 var childInjector = injector.createChild(null, name: 'bar'); |
| 425 expect(childInjector.name, 'bar'); |
| 426 }); |
| 427 |
| 428 |
| 429 it('should instantiate class only once (Issue #18)', () { |
| 430 var injector = injectorFactory([ |
| 431 new Module() |
| 432 ..type(Log) |
| 433 ..type(ClassOne) |
| 434 ..factory(InterfaceOne, (i) => i.get(ClassOne)) |
| 435 ]); |
| 436 |
| 437 expect(injector.get(InterfaceOne), same(injector.get(ClassOne))); |
| 438 expect(injector.get(Log).log.join(' '), 'ClassOne'); |
| 439 }); |
| 440 |
| 441 |
| 442 describe('creation strategy', () { |
| 443 |
| 444 it('should get called for instance creation', () { |
| 445 |
| 446 List creationLog = []; |
| 447 dynamic creation(Injector requesting, Injector defining, factory) { |
| 448 creationLog.add([requesting, defining]); |
| 449 return factory(); |
| 450 } |
| 451 |
| 452 var parentModule = new Module() |
| 453 ..type(Engine, implementedBy: MockEngine, creation: creation) |
| 454 ..type(Car, creation: creation); |
| 455 |
| 456 var parentInjector = injectorFactory([parentModule]); |
| 457 var childInjector = parentInjector.createChild([]); |
| 458 childInjector.get(Car); |
| 459 expect(creationLog, [ |
| 460 [childInjector, parentInjector], |
| 461 [childInjector, parentInjector] |
| 462 ]); |
| 463 }); |
| 464 |
| 465 it('should be able to prevent instantiation', () { |
| 466 |
| 467 List creationLog = []; |
| 468 dynamic creation(Injector requesting, Injector defining, factory) { |
| 469 throw 'not allowing'; |
| 470 } |
| 471 |
| 472 var module = new Module() |
| 473 ..type(Engine, implementedBy: MockEngine, creation: creation); |
| 474 var injector = injectorFactory([module]); |
| 475 expect(() { |
| 476 injector.get(Engine); |
| 477 }, throwsA('not allowing')); |
| 478 }); |
| 479 }); |
| 480 |
| 481 |
| 482 describe('visiblity', () { |
| 483 |
| 484 it('should hide instances', () { |
| 485 |
| 486 var rootMock = new MockEngine(); |
| 487 var childMock = new MockEngine(); |
| 488 |
| 489 var parentModule = new Module() |
| 490 ..value(Engine, rootMock); |
| 491 var childModule = new Module() |
| 492 ..value(Engine, childMock, visibility: (_, __) => false); |
| 493 |
| 494 var parentInjector = injectorFactory([parentModule]); |
| 495 var childInjector = parentInjector.createChild([childModule]); |
| 496 |
| 497 var val = childInjector.get(Engine); |
| 498 expect(val, same(rootMock)); |
| 499 }); |
| 500 |
| 501 it('should throw when an instance in not visible in the root injector', ()
{ |
| 502 var module = new Module() |
| 503 ..value(Car, 'Invisible', visibility: (_, __) => false); |
| 504 |
| 505 var injector = injectorFactory([module]); |
| 506 |
| 507 expect(() { |
| 508 injector.get(Car); |
| 509 }, toThrow( |
| 510 NoProviderError, |
| 511 'No provider found for Car! (resolving Car)' |
| 512 )); |
| 513 }); |
| 514 |
| 515 }); |
| 516 |
| 517 }); |
| 518 |
| 519 } |
| OLD | NEW |