Beispiel #1
0
func TestErrCyclicDependency(t *testing.T) {

	Convey("Given I have cyclic dependencies", t, func() {
		injector := katana.New().ProvideNew(&DepA{}, func(depB *DepB, depD *DepD) *DepA {
			return &DepA{depB, depD}
		})

		injector.ProvideNew(&DepB{}, func() *DepB {
			return &DepB{}
		})

		injector.ProvideNew(&DepC{}, func(dep *DepA) *DepC {
			return &DepC{dep}
		})

		injector.ProvideNew(&DepD{}, func(dep *DepC) *DepD {
			return &DepD{dep}
		})

		Convey("When I resolve the cyclic dependency", func() {
			var dep *DepA
			resolveWithCyclicDependency := func() {
				injector.Resolve(&dep)
			}

			Convey("Then all dependencies are resolved recursively", func() {
				So(resolveWithCyclicDependency, should.Panic)
			})
		})
	})
}
Beispiel #2
0
func TestKatanaProvide(t *testing.T) {
	Convey("Given I have an instance of katana injector with a few value providers", t, func() {
		depA := &Dependency{}
		depB := Dependency{}

		injector := katana.New().Provide(depA, depB)

		Convey("When I resolve instances of the provided values", func() {
			var depA1, depA2 *Dependency
			var depB1, depB2 Dependency

			injector.Resolve(&depA1, &depA2, &depB1, &depB2)

			Convey("Then instances of the same type are the same", func() {
				So(depA1, should.NotBeNil)
				So(depA2, should.NotBeNil)
				So(depA1, should.Equal, depA2)
				So(depB1, should.NotBeNil)
				So(depB2, should.NotBeNil)
				So(depB1, should.NotEqual, depB2)
				So(depB1, should.Resemble, depB2)
			})
		})
	})
}
Beispiel #3
0
func TestKatanaProvideNewInstance(t *testing.T) {
	Convey("Given I have an instance of katana injector with a new instance provider of a dependency", t, func() {
		injector := katana.New().ProvideNew(&Dependency{}, func() *Dependency {
			return &Dependency{}
		})

		Convey("When I resolve multiple instances of the provided dependency", func() {
			var dep1, dep2 *Dependency
			injector.Resolve(&dep1, &dep2)

			Convey("Then the resolved dependnecies points to different memory address", func() {
				So(dep1, should.NotEqual, dep2)
			})
		})
	})

	Convey("Given I have two new instance providers one for *Dependency and another one for *DependencyB", t, func() {
		injector := katana.New().ProvideNew(&Dependency{}, func() *Dependency {
			return &Dependency{}
		})

		injector.ProvideNew(&DependencyA{}, func(dep *Dependency) *DependencyA {
			return &DependencyA{dep}
		})

		Convey("When I resolve multiple instances of *DependencyB which depends on *Dependency", func() {
			var dep1, dep2 *DependencyA
			injector.Resolve(&dep1, &dep2)

			Convey("Then the resolved dependencies point to different memory addresses", func() {
				So(dep1, should.NotEqual, dep2)

				Convey("And its dependencies also point to different memory addresses", func() {
					So(dep1.Dep, should.NotBeNil)
					So(dep2.Dep, should.NotBeNil)
					So(dep1.Dep, should.NotEqual, dep2.Dep)
				})
			})
		})
	})
}
Beispiel #4
0
func TestErrInvalidReference(t *testing.T) {
	Convey("Given I have a provider registered for a given dependency", t, func() {
		injector := katana.New().ProvideNew(Dependency{}, func() *Dependency {
			return &Dependency{}
		})

		Convey("Then it panics resolving a reference that is a zero value", func() {
			var ref *Dependency
			resolvingZeroValue := func() { injector.Resolve(ref) }

			So(resolvingZeroValue, should.Panic)
		})
	})
}
Beispiel #5
0
func TestErrInvalidProvider(t *testing.T) {
	// TODO write test case to catch whether the error is properly built
	Convey("Given I have a provider function with no return value for a given dependency", t, func() {
		invalidProvider := func() {
			katana.New().ProvideNew(&DependencyC{}, func() {})
		}

		Convey("Then it fails with an invalid provider error", func() {
			So(invalidProvider, should.Panic)
		})
	})

	Convey("Given I have a provider function with multiple return values for a given dependency", t, func() {
		invalidProvider := func() {
			katana.New().ProvideNew(&DependencyC{}, func() (*DependencyC, error) {
				return nil, nil
			})
		}

		Convey("Then it fails with an invalid provider error", func() {
			So(invalidProvider, should.Panic)
		})
	})
}
Beispiel #6
0
func TestKatanaProvidesSingletonInstance(t *testing.T) {
	Convey("Given I have an instance of katana injector with a singleton dependency provider", t, func() {
		injector := katana.New().ProvideSingleton(&Dependency{}, func() *Dependency {
			return &Dependency{}
		})

		Convey("When I resolve multiple instances of the provided dependency", func() {
			var dep1, dep2 *Dependency
			injector.Resolve(&dep1, &dep2)

			Convey("Then the resolved dependencies points to the same memory address", func() {
				So(dep1, should.Equal, dep2)
			})
		})
	})
}
Beispiel #7
0
func TestInjectorClone(t *testing.T) {
	Convey("Given I have an injector with a few injectables and cached instances", t, func() {
		injector := katana.New()

		injector.Provide(&DependencyB{})

		injector.ProvideNew(&Dependency{}, func() *Dependency {
			return &Dependency{}
		})

		injector.ProvideSingleton(&DependencyA{}, func(dep *Dependency) *DependencyA {
			return &DependencyA{dep}
		})

		Convey("When I clone the injector", func() {
			newInjector := injector.Clone()

			Convey("Then the new injector inherits the original injector providers", func() {
				var dep *Dependency
				var depA *DependencyA
				var depB *DependencyB
				newInjector.Resolve(&dep, &depA, &depB)

				So(dep, should.NotBeNil)
				So(depA, should.NotBeNil)
				So(depB, should.NotBeNil)
			})

			Convey("And I register new providers with the new injector", func() {
				newInjector.ProvideSingleton((*InterfaceDependency)(nil), func() InterfaceDependency {
					return &InterfaceDependencyImpl{}
				})

				Convey("Then the provider is available only in the cloned injector", func() {
					var dep1, dep2 InterfaceDependency
					newInjector.Resolve(&dep1)

					So(dep1, should.NotBeNil)

					resolveWithOriginalInjector := func() { injector.Resolve(&dep2) }

					So(resolveWithOriginalInjector, should.Panic)
				})
			})
		})
	})
}
Beispiel #8
0
func TestKatanaProvideAs(t *testing.T) {
	Convey("Given I have an instance of katana injector with an instance provided as an interface type", t, func() {
		dep := &InterfaceDependencyImpl{}

		injector := katana.New().ProvideAs((*InterfaceDependency)(nil), dep)

		Convey("When I resolve a reference to the provided dependency", func() {
			var depA InterfaceDependency

			injector.Resolve(&depA)

			Convey("Then the reference is successfully resolved", func() {
				So(depA, should.NotBeNil)
				So(depA, should.Equal, dep)
			})
		})
	})
}
Beispiel #9
0
func TestKatanaProvidesNewInstanceOfInterfaceType(t *testing.T) {
	Convey("Given I have a provider of an interface dependency type", t, func() {
		injector := katana.New().ProvideNew((*InterfaceDependency)(nil), func() InterfaceDependency {
			return &InterfaceDependencyImpl{}
		})

		Convey("When I resolve multiple instances of the provided dependency", func() {
			var dep1, dep2 InterfaceDependency
			injector.Resolve(&dep1, &dep2)

			Convey("Then the dependencies are resolved", func() {
				So(dep1, should.NotEqual, dep2)
				So(dep1, should.HaveSameTypeAs, &InterfaceDependencyImpl{})
				So(dep2, should.HaveSameTypeAs, &InterfaceDependencyImpl{})
			})
		})
	})
}
Beispiel #10
0
func TestErrProviderAlreadyRegistered(t *testing.T) {
	Convey("Given I have a provider registered for a given dependency", t, func() {
		injector := katana.New().ProvideNew(&DependencyC{}, func() *DependencyC {
			return &DependencyC{}
		})

		Convey("When I register another provider for that same dependency type", func() {
			alreadyRegisteredProvider := func() {
				injector.ProvideNew(&DependencyC{}, func() *DependencyC {
					return &DependencyC{}
				})
			}

			Convey("Then it fails with an already registered provider", func() {
				So(alreadyRegisteredProvider, should.Panic)
			})
		})
	})
}
Beispiel #11
0
func ExampleKatanaAPI() {
	// Grabs a new instance of katana.Injector
	injector := katana.New()

	// Registers the given instance of Config to be provided as a singleton injectable
	injector.Provide(Config{
		DatastoreURL: "https://myawesomestartup.com/db",
		CacheTTL:     20000,
	})

	// Registers a constructor function that always provides a new instance of *Cache
	injector.ProvideNew(&Cache{}, func(config Config) *Cache {
		return &Cache{config.CacheTTL}
	})

	// Registers a constructor function that always provides a new instance of *Datastore
	// resolving its dependencies -- Config and *Cache -- as part of the process
	injector.ProvideNew(&Datastore{}, func(config Config, cache *Cache) *Datastore {
		return &Datastore{cache, config.DatastoreURL}
	})

	// Registers a constructor function that lazily provides the same instance of *AccountService
	// resolving its dependencies -- *Datastore -- as part of the process.
	injector.ProvideSingleton(&AccountService{}, func(db *Datastore) *AccountService {
		return &AccountService{db}
	})

	var service1, service2 *AccountService
	injector.Resolve(&service1, &service2)

	fmt.Println("service1 == service2:", service1 == service2)
	fmt.Println("service1.Datastore.URL:", service1.Datastore.URL)
	fmt.Println("service1.Datastore.Cache.TTL:", service1.Datastore.Cache.TTL)

	// Output:
	// service1 == service2: true
	// service1.Datastore.URL: https://myawesomestartup.com/db
	// service1.Datastore.Cache.TTL: 20000
}
Beispiel #12
0
func ExampleHTTP() {
	injector := katana.New().
		ProvideNew(&Database{}, NewDatabase).
		ProvideNew(&Renderer{}, NewRenderer)

	http.HandleFunc("/users", func(w http.ResponseWriter, req *http.Request) {
		var db *Database
		var render *Renderer

		// Clone creates a copy of the injector, isolating the new registered providers form other threads
		// We don't want users sharing each other's requests/response writers...
		injector.Clone().
			ProvideAs((*http.ResponseWriter)(nil), w).
			Provide(req).
			Resolve(&render, &db)

		render.JSON(200, db.AllUsers())
	})

	go func() {
		log.Fatal(http.ListenAndServe(":8080", nil))
	}()

	done := make(chan bool)
	go func() {
		res, _ := http.Get("http://localhost:8080/users")
		bytes, _ := ioutil.ReadAll(res.Body)

		var users []*User
		json.Unmarshal(bytes, &users)

		fmt.Printf("Users: %v, %v", users[0].Name, users[1].Name)
		// Output: Users: borges, diego
		done <- true
	}()

	<-done
}
Beispiel #13
0
func TestKatanaResolvesTransitiveDependencies(t *testing.T) {
	Convey("Given I have transitive dependencies", t, func() {
		injector := katana.New().ProvideNew(&DependencyB{}, func(dep *DependencyA) *DependencyB {
			return &DependencyB{dep}
		})

		injector.ProvideNew(&DependencyA{}, func(dep *Dependency) *DependencyA {
			return &DependencyA{dep}
		})

		injector.Provide(&Dependency{})

		Convey("When I resolve the root dep", func() {
			var depA *DependencyB
			injector.Resolve(&depA)

			Convey("Then all dependencies are resolved recursively", func() {
				So(depA, should.NotBeNil)
				So(depA.Dep, should.NotBeNil)
				So(depA.Dep.Dep, should.NotBeNil)
			})
		})
	})
}