diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index 133a97a9..9fb5b08e 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -27,6 +27,85 @@ describe("Env", function() { expect(fakeReporter.jasmineStarted).toHaveBeenCalled(); }); }); + + it('removes all spies when env is executed', function(done) { + originalFoo = function() {}, + testObj = { + foo: originalFoo + }, + firstSpec = jasmine.createSpy('firstSpec').and.callFake(function() { + env.spyOn(testObj, 'foo'); + }), + secondSpec = jasmine.createSpy('secondSpec').and.callFake(function() { + expect(testObj.foo).toBe(originalFoo); + }); + env.describe('test suite', function() { + env.it('spec 0', firstSpec); + env.it('spec 1', secondSpec); + }); + + var assertions = function() { + expect(firstSpec).toHaveBeenCalled(); + expect(secondSpec).toHaveBeenCalled(); + done(); + }; + + env.addReporter({ jasmineDone: assertions }); + + env.execute(); + }); + + describe("#spyOn", function() { + it("checks for the existance of the object", function() { + expect(function() { + env.spyOn(void 0, 'pants'); + }).toThrowError(/could not find an object/); + }); + + it("checks for the existance of the method", function() { + var subject = {}; + + expect(function() { + env.spyOn(subject, 'pants'); + }).toThrowError(/method does not exist/); + }); + + it("checks if it has already been spied upon", function() { + var subject = { spiedFunc: function() {} }; + + env.spyOn(subject, 'spiedFunc'); + + expect(function() { + env.spyOn(subject, 'spiedFunc'); + }).toThrowError(/has already been spied upon/); + }); + + it("overrides the method on the object and returns the spy", function() { + var originalFunctionWasCalled = false; + var subject = { spiedFunc: function() { originalFunctionWasCalled = true; } }; + + originalFunc = subject.spiedFunc; + + var spy = env.spyOn(subject, 'spiedFunc'); + + expect(subject.spiedFunc).toEqual(spy); + + expect(subject.spiedFunc.calls.any()).toEqual(false); + expect(subject.spiedFunc.calls.count()).toEqual(0); + + subject.spiedFunc('foo'); + + expect(subject.spiedFunc.calls.any()).toEqual(true); + expect(subject.spiedFunc.calls.count()).toEqual(1); + expect(subject.spiedFunc.calls.mostRecent().args).toEqual(['foo']); + expect(subject.spiedFunc.calls.mostRecent().object).toEqual(subject); + expect(originalFunctionWasCalled).toEqual(false); + + subject.spiedFunc('bar'); + expect(subject.spiedFunc.calls.count()).toEqual(2); + expect(subject.spiedFunc.calls.mostRecent().args).toEqual(['bar']); + }); + }); describe("#catchException", function() { it("returns true if the exception is a pending spec exception", function() { diff --git a/spec/core/SpySpec.js b/spec/core/SpySpec.js index 8ab87da1..c42db911 100644 --- a/spec/core/SpySpec.js +++ b/spec/core/SpySpec.js @@ -1,210 +1,31 @@ describe('Spies', function () { - var env; - beforeEach(function() { - env = new j$.Env(); - }); + describe("createSpy", function() { + var TestClass; - it('should replace the specified function with a spy object', function() { - var originalFunctionWasCalled = false; - var TestClass = { - someFunction: function() { - originalFunctionWasCalled = true; - } - }; - env.spyOn(TestClass, 'someFunction'); + beforeEach(function() { + TestClass = function() {}; + TestClass.prototype.someFunction = function() {}; + TestClass.prototype.someFunction.bob = "test"; + }); + + it("preserves the properties of the spied function", function() { + var spy = j$.createSpy(TestClass.prototype, TestClass.prototype.someFunction); - expect(TestClass.someFunction.calls.any()).toEqual(false); - expect(TestClass.someFunction.calls.count()).toEqual(0); - - TestClass.someFunction('foo'); - - expect(TestClass.someFunction.calls.any()).toEqual(true); - expect(TestClass.someFunction.calls.count()).toEqual(1); - expect(TestClass.someFunction.calls.mostRecent().args).toEqual(['foo']); - expect(TestClass.someFunction.calls.mostRecent().object).toEqual(TestClass); - expect(originalFunctionWasCalled).toEqual(false); - - TestClass.someFunction('bar'); - expect(TestClass.someFunction.calls.count()).toEqual(2); - expect(TestClass.someFunction.calls.mostRecent().args).toEqual(['bar']); - }); - - it('should allow you to view args for a particular call', function() { - var originalFunctionWasCalled = false; - var TestClass = { - someFunction: function() { - originalFunctionWasCalled = true; - } - }; - env.spyOn(TestClass, 'someFunction'); - - TestClass.someFunction('foo'); - TestClass.someFunction('bar'); - expect(TestClass.someFunction.calls.first().args).toEqual(['foo']); - expect(TestClass.someFunction.calls.mostRecent().args).toEqual(['bar']); - }); - - it('should be possible to call through to the original method, or return a specific result', function() { - var originalFunctionWasCalled = false; - var passedArgs; - var passedObj; - var TestClass = { - someFunction: function() { - originalFunctionWasCalled = true; - passedArgs = Array.prototype.slice.call(arguments, 0); - passedObj = this; - return "return value from original function"; - } - }; - - env.spyOn(TestClass, 'someFunction').and.callThrough(); - var result = TestClass.someFunction('arg1', 'arg2'); - expect(result).toEqual("return value from original function"); - expect(originalFunctionWasCalled).toEqual(true); - expect(passedArgs).toEqual(['arg1', 'arg2']); - expect(passedObj).toEqual(TestClass); - expect(TestClass.someFunction.calls.any()).toEqual(true); - }); - - it('should be possible to return a specific value', function() { - var originalFunctionWasCalled = false; - var TestClass = { - someFunction: function() { - originalFunctionWasCalled = true; - return "return value from original function"; - } - }; - - env.spyOn(TestClass, 'someFunction').and.callReturn("some value"); - originalFunctionWasCalled = false; - var result = TestClass.someFunction('arg1', 'arg2'); - expect(result).toEqual("some value"); - expect(originalFunctionWasCalled).toEqual(false); - }); - - it('should be possible to throw a specific error', function() { - var originalFunctionWasCalled = false; - var TestClass = { - someFunction: function() { - originalFunctionWasCalled = true; - return "return value from original function"; - } - }; - - env.spyOn(TestClass, 'someFunction').and.callThrow(new Error('fake error')); - var exception; - try { - TestClass.someFunction('arg1', 'arg2'); - } catch (e) { - exception = e; - } - expect(exception.message).toEqual('fake error'); - expect(originalFunctionWasCalled).toEqual(false); - }); - - it('should be possible to call a specified function', function() { - var originalFunctionWasCalled = false; - var fakeFunctionWasCalled = false; - var passedArgs; - var passedObj; - var TestClass = { - someFunction: function() { - originalFunctionWasCalled = true; - return "return value from original function"; - } - }; - - env.spyOn(TestClass, 'someFunction').and.callFake(function() { - fakeFunctionWasCalled = true; - passedArgs = Array.prototype.slice.call(arguments, 0); - passedObj = this; - return "return value from fake function"; + expect(spy.bob).toEqual("test"); }); - var result = TestClass.someFunction('arg1', 'arg2'); - expect(result).toEqual("return value from fake function"); - expect(originalFunctionWasCalled).toEqual(false); - expect(fakeFunctionWasCalled).toEqual(true); - expect(passedArgs).toEqual(['arg1', 'arg2']); - expect(passedObj).toEqual(TestClass); - expect(TestClass.someFunction.calls.any()).toEqual(true); - }); - - it('removes all spies when env is executed', function(done) { - var env = new j$.Env(), - originalFoo = function() {}, - testObj = { - foo: originalFoo - }, - firstSpec = jasmine.createSpy('firstSpec').and.callFake(function() { - env.spyOn(testObj, 'foo'); - }), - secondSpec = jasmine.createSpy('secondSpec').and.callFake(function() { - expect(testObj.foo).toBe(originalFoo); - }); - env.describe('test suite', function() { - env.it('spec 0', firstSpec); - env.it('spec 1', secondSpec); + it("warns the user that we indend to overwrite an existing property", function() { + expect(function() { + j$.createSpy(TestClass.prototype, TestClass.prototype.someFunction); + }).toThrowError("Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon"); }); - var assertions = function() { - expect(firstSpec).toHaveBeenCalled(); - expect(secondSpec).toHaveBeenCalled(); - done(); - }; + it("adds a spyStrategy and callTracker to the spy", function() { + var spy = j$.createSpy(TestClass.prototype, TestClass.prototype.someFunction); - env.addReporter({ jasmineDone: assertions }); - - env.execute(); - }); - - it('throws an exception when some method is spied on twice', function() { - var TestClass = { someFunction: function() { - } }; - env.spyOn(TestClass, 'someFunction'); - var exception; - try { - env.spyOn(TestClass, 'someFunction'); - } catch (e) { - exception = e; - } - expect(exception).toBeDefined(); - }); - - it('to spy on an undefined method throws exception', function() { - var TestClass = { - someFunction : function() { - } - }; - function efunc() { - env.spyOn(TestClass, 'someOtherFunction'); - } - - expect(function() { - efunc(); - }).toThrow('someOtherFunction() method does not exist'); - }); - - it('should be able to reset a spy', function() { - var TestClass = { someFunction: function() {} }; - env.spyOn(TestClass, 'someFunction'); - - expect(TestClass.someFunction).not.toHaveBeenCalled(); - TestClass.someFunction(); - expect(TestClass.someFunction).toHaveBeenCalled(); - TestClass.someFunction.calls.reset(); - expect(TestClass.someFunction).not.toHaveBeenCalled(); - expect(TestClass.someFunction.calls.count()).toEqual(0); - }); - - it("preserves the properties of the spied function", function() { - var TestClass = function() {}; - TestClass.prototype.someFunction = function() {}; - TestClass.prototype.someFunction.bob = "test"; - - var spy = env.spyOn(TestClass.prototype, 'someFunction'); - - expect(spy.bob).toEqual("test"); + expect(spy.and).toEqual(jasmine.any(j$.SpyStrategy); + expect(spy.calls).toEqual(jasmine.any(j$.CallTracker); + }); }); describe("createSpyObj", function() { diff --git a/src/core/Env.js b/src/core/Env.js index 67c0eef8..e21ea96f 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -211,16 +211,16 @@ getJasmineRequireObj().Env = function(j$) { this.spyOn = function(obj, methodName) { if (j$.util.isUndefined(obj)) { - throw "spyOn could not find an object to spy upon for " + methodName + "()"; + throw new Error("spyOn could not find an object to spy upon for " + methodName + "()"); } if (j$.util.isUndefined(obj[methodName])) { - throw methodName + '() method does not exist'; + throw new Error(methodName + '() method does not exist'); } if (obj[methodName] && j$.isSpy(obj[methodName])) { //TODO?: should this return the current spy? Downside: may cause user confusion about spy state - throw methodName + ' has already been spied upon'; + throw new Error(methodName + ' has already been spied upon'); } var spy = j$.createSpy(methodName, obj[methodName]); diff --git a/src/core/base.js b/src/core/base.js index f4e106c7..b490d0e1 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -65,13 +65,17 @@ getJasmineRequireObj().base = function(j$) { return spyStrategy.exec.apply(this, arguments); }; - spy.and = spyStrategy; - spy.calls = callTracker; + for (var prop in originalFn) { + if (prop === 'and' || prop === 'calls') { + throw new Error("Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon"); + } - for (prop in originalFn) { spy[prop] = originalFn[prop]; } + spy.and = spyStrategy; + spy.calls = callTracker; + return spy; };