summaryrefslogtreecommitdiff
path: root/injector-tests.js
diff options
context:
space:
mode:
Diffstat (limited to 'injector-tests.js')
-rw-r--r--injector-tests.js200
1 files changed, 200 insertions, 0 deletions
diff --git a/injector-tests.js b/injector-tests.js
new file mode 100644
index 0000000..339034f
--- /dev/null
+++ b/injector-tests.js
@@ -0,0 +1,200 @@
+/*global describe,it,expect,beforeEach*/
+describe("injector", function() {
+ var injector;
+ beforeEach(function() {
+ injector = new Injector();
+ });
+
+ describe("#register()", function() {
+ it("should return the injector", function() {
+ var result = injector.register("A", function(){ return "A"; });
+ expect(result).toBe(injector);
+ });
+
+ it("should use a function's name if no name provided", function() {
+ injector.register(function A(){ return "A"; });
+ expect(injector.specs.A).not.toBe(undefined); // IMPLEMENTATION DETAIL
+ });
+
+ it("should register an object as a series of dependencies", function() {
+ injector.register({
+ A: function(){ return "A"; },
+ B: function(){ return "B"; }
+ });
+ expect(injector.specs.A).not.toBe(undefined); // IMPLEMENTATION DETAIL
+ expect(injector.specs.B).not.toBe(undefined); // IMPLEMENTATION DETAIL
+ });
+ });
+
+ describe("#get()", function() {
+ it("should throw if the dependency can't be found", function() {
+ expect(function() {
+ injector.get("A");
+ }).toThrow();
+ });
+ });
+
+ describe("#invoke()", function() {
+ it("should be able to invoke a function with no dependencies", function() {
+ var A = injector.invoke(function() {return "functionResult";});
+ expect(A).toBe("functionResult");
+ });
+
+ it("should be able to invoke a function with a dependency in an array", function() {
+ injector.register("B", function() {return "constructorB";});
+ var A = injector.invoke(["B", function(B) {return B + "A";}]);
+ expect(A).toBe("constructorBA");
+ });
+
+ it("should be able to invoke a function with an implicit dependency", function() {
+ injector.register("B", function() {return "constructorB";});
+ var A = injector.invoke(function(B) {return B + "A";});
+ expect(A).toBe("constructorBA");
+ });
+
+ it("should not touch any exceptions thrown by the function", function() {
+ expect(function() {
+ throw new Error("An error");
+ }).toThrow(new Error("An error"));
+ });
+
+ it("should invoke the function with 'this' set to the injector", function() {
+ var hasRun = false;
+ injector.invoke(function() {
+ hasRun = true;
+ expect(this).toBe(injector);
+ });
+ expect(hasRun).toBe(true);
+ });
+
+ it("should not provide 'this.requestor' to the invoked function", function() {
+ var caught = {};
+ try {
+ injector.invoke(function() {
+ this.requestor();
+ });
+ } catch (e) {
+ caught = e;
+ }
+ expect(caught.name).toBe("InjectorError");
+ });
+ });
+
+ describe("constructor dependency injection", function() {
+ it("should be able to register a constructor with no dependencies, and get the value for it", function() {
+ injector.register("A", function() {return "constructorA";});
+
+ var A = injector.get("A");
+ expect(A).toBe("constructorA");
+ });
+
+ it("should be able to register a constructor with a dependency in an array, and get the value for it", function() {
+ injector.register("B", function() {return "constructorB";});
+ injector.register("A", ["B", function(B) {return B;}]);
+
+ var A = injector.get("A");
+ expect(A).toBe("constructorB");
+ });
+
+ it("should be able to register a constructor with an implicit dependency, and get the value for it", function() {
+ injector.register("B", function() {return "constructorB";});
+ injector.register("A", function(B) {return B;});
+
+ var A = injector.get("A");
+ expect(A).toBe("constructorB");
+ });
+
+ it("should be able to register a constructor with an implicit name", function() {
+ injector.register(function A() {return "constructorA";});
+ var A = injector.get("A");
+ expect(A).toBe("constructorA");
+ });
+
+ it("should be able to register constructors with an object", function() {
+ injector.register({
+ A: ["B", function (B) {return "constructorA" + B;}],
+ B: function(C) { return "B" + C; },
+ C: function() { return "C"; }
+ });
+ var A = injector.get("A");
+ expect(A).toBe("constructorABC");
+ });
+
+ it("should wrap any exceptions thrown by constructors", function() {
+ var error = new Error("constructorA");
+ injector.register("A", function() {throw error;});
+ var caught = {};
+ try {
+ injector.get("A");
+ } catch (e) {
+ caught = e;
+ }
+ expect(caught.name).toBe("InjectorError");
+ expect(caught.cause).toBe(error);
+ });
+
+ it("should detect circular dependencies", function() {
+ injector.register("A", ["B", function(B) {return null;}]);
+ injector.register("B", ["A", function(A) {return null;}]);
+
+ var caught = {message: ''};
+ try {
+ injector.get("A");
+ } catch (e) {
+ caught = e;
+ }
+ expect(caught.message.indexOf("Cyclic dependency detected")).toBe(0);
+ });
+
+ it("should provide access to the requestor through 'this.requestor()'", function() {
+ injector.register("B", function() {
+ expect(this.requestor()).toBe("A");
+ return "returnValueB";
+ });
+ injector.register("A", ["B", function(B) {
+ return B + "A";
+ }]);
+
+ // make sure the function actually ran
+ expect(injector.get("A")).toBe("returnValueBA");
+ });
+ });
+
+ describe("caching", function() {
+ it("should return the same exact value for multiple requests of the same dependency", function() {
+ injector.register("A", function() {return {an: 'object'};});
+
+ var first = injector.get("A");
+ var second = injector.get("A");
+ expect(first).toBe(second);
+ });
+
+ it("shouldn't re-run constructors for cached values", function() {
+ var numTimes = 0;
+ injector.register("A", function() {
+ numTimes++;
+ if (numTimes > 1)
+ throw new Error("Shouldn't be run more than once");
+ return {an: 'object'};
+ });
+
+ var first = injector.get("A");
+ var second = injector.get("A");
+ expect(first).toBe(second);
+ });
+
+ it("shouldn't cache values which depend on their requestor", function() {
+ var numTimes = 0;
+ injector.register("A", function() {
+ numTimes++;
+ return "A" + this.requestor();
+ });
+ injector.register("B", ["A", function(A) {return "B" + A;}]);
+ injector.register("C", ["A", function(A) {return "C" + A;}]);
+
+ expect(injector.get("B")).toBe("BAB");
+ expect(injector.get("C")).toBe("CAC");
+ expect(numTimes).toBe(2);
+ });
+ });
+});