Merge branch 'custom-spy-strategy' into 3.0-features

This commit is contained in:
Gregg Van Hove
2018-01-22 12:12:59 -08:00
18 changed files with 569 additions and 187 deletions

View File

@@ -70,6 +70,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) {
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
j$.Spec = jRequire.Spec(j$);
j$.Spy = jRequire.Spy(j$);
j$.SpyFactory = jRequire.SpyFactory(j$);
j$.SpyRegistry = jRequire.SpyRegistry(j$);
j$.SpyStrategy = jRequire.SpyStrategy(j$);
j$.StringMatching = jRequire.StringMatching(j$);
@@ -305,18 +306,6 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return new j$.ArrayWithExactContents(sample);
};
/**
* Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it.
* @name jasmine.createSpy
* @function
* @param {String} [name] - Name to give the spy. This will be displayed in failure messages.
* @param {Function} [originalFn] - Function to act as the real implementation.
* @return {Spy}
*/
j$.createSpy = function(name, originalFn) {
return j$.Spy(name, originalFn);
};
j$.isSpy = function(putativeSpy) {
if (!putativeSpy) {
return false;
@@ -324,47 +313,6 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return putativeSpy.and instanceof j$.SpyStrategy &&
putativeSpy.calls instanceof j$.CallTracker;
};
/**
* Create an object with multiple {@link Spy}s as its members.
* @name jasmine.createSpyObj
* @function
* @param {String} [baseName] - Base name for the spies in the object.
* @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}.
* @return {Object}
*/
j$.createSpyObj = function(baseName, methodNames) {
var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName);
if (baseNameIsCollection && j$.util.isUndefined(methodNames)) {
methodNames = baseName;
baseName = 'unknown';
}
var obj = {};
var spiesWereSet = false;
if (j$.isArray_(methodNames)) {
for (var i = 0; i < methodNames.length; i++) {
obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]);
spiesWereSet = true;
}
} else if (j$.isObject_(methodNames)) {
for (var key in methodNames) {
if (methodNames.hasOwnProperty(key)) {
obj[key] = j$.createSpy(baseName + '.' + key);
obj[key].and.returnValue(methodNames[key]);
spiesWereSet = true;
}
}
}
if (!spiesWereSet) {
throw 'createSpyObj requires a non-empty array or object of method names to create spies for';
}
return obj;
};
};
getJasmineRequireObj().util = function(j$) {
@@ -850,6 +798,13 @@ getJasmineRequireObj().Env = function(j$) {
return true;
};
this.addSpyStrategy = function(name, fn) {
if(!currentRunnable()) {
throw new Error('Custom spy strategies must be added in a before function or a spec');
}
runnableResources[currentRunnable().id].customSpyStrategies[name] = fn;
};
this.addCustomEqualityTester = function(tester) {
if(!currentRunnable()) {
throw new Error('Custom Equalities must be added in a before function or a spec');
@@ -894,7 +849,7 @@ getJasmineRequireObj().Env = function(j$) {
};
var defaultResourcesForRunnable = function(id, parentRunnableId) {
var resources = {spies: [], customEqualityTesters: [], customMatchers: {}};
var resources = {spies: [], customEqualityTesters: [], customMatchers: {}, customSpyStrategies: {}};
if(runnableResources[parentRunnableId]){
resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters);
@@ -1135,12 +1090,27 @@ getJasmineRequireObj().Env = function(j$) {
reporter.clearReporters();
};
var spyRegistry = new j$.SpyRegistry({currentSpies: function() {
if(!currentRunnable()) {
throw new Error('Spies must be created in a before function or a spec');
var spyFactory = new j$.SpyFactory(function() {
var runnable = currentRunnable();
if (runnable) {
return runnableResources[runnable.id].customSpyStrategies;
}
return runnableResources[currentRunnable().id].spies;
}});
return {};
});
var spyRegistry = new j$.SpyRegistry({
currentSpies: function() {
if(!currentRunnable()) {
throw new Error('Spies must be created in a before function or a spec');
}
return runnableResources[currentRunnable().id].spies;
},
createSpy: function(name, originalFn) {
return self.createSpy(name, originalFn);
}
});
this.allowRespy = function(allow){
spyRegistry.allowRespy(allow);
@@ -1154,6 +1124,14 @@ getJasmineRequireObj().Env = function(j$) {
return spyRegistry.spyOnProperty.apply(spyRegistry, arguments);
};
this.createSpy = function(name, originalFn) {
return spyFactory.createSpy(name, originalFn);
};
this.createSpyObj = function(baseName, methodNames) {
return spyFactory.createSpyObj(baseName, methodNames);
};
var ensureIsFunction = function(fn, caller) {
if (!j$.isFunction_(fn)) {
throw new Error(caller + ' expects a function argument; received ' + j$.getType_(fn));
@@ -4946,6 +4924,43 @@ getJasmineRequireObj().interface = function(jasmine, env) {
return env.clock;
};
/**
* Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it.
* @name jasmine.createSpy
* @function
* @param {String} [name] - Name to give the spy. This will be displayed in failure messages.
* @param {Function} [originalFn] - Function to act as the real implementation.
* @return {Spy}
*/
jasmine.createSpy = function(name, originalFn) {
return env.createSpy(name, originalFn);
};
/**
* Create an object with multiple {@link Spy}s as its members.
* @name jasmine.createSpyObj
* @function
* @param {String} [baseName] - Base name for the spies in the object.
* @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}.
* @return {Object}
*/
jasmine.createSpyObj = function(baseName, methodNames) {
return env.createSpyObj(baseName, methodNames);
};
/**
* Add a custom spy strategy for the current scope of specs.
*
* _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}.
* @name jasmine.addSpyStrategy
* @function
* @param {String} name - The name of the strategy (i.e. what you call from `and`)
* @param {Function} factory - Factory function that returns the plan to be executed.
*/
jasmine.addSpyStrategy = function(name, factory) {
return env.addSpyStrategy(identifier, factory);
};
return jasmineInterface;
};
@@ -4964,7 +4979,7 @@ getJasmineRequireObj().Spy = function (j$) {
* @constructor
* @name Spy
*/
function Spy(name, originalFn) {
function Spy(name, originalFn, customStrategies) {
var numArgs = (typeof originalFn === 'function' ? originalFn.length : 0),
wrapper = makeFunc(numArgs, function () {
return spy.apply(this, Array.prototype.slice.call(arguments));
@@ -4974,7 +4989,8 @@ getJasmineRequireObj().Spy = function (j$) {
fn: originalFn,
getSpy: function () {
return wrapper;
}
},
customStrategies: customStrategies
}),
callTracker = new j$.CallTracker(),
spy = function () {
@@ -5112,6 +5128,52 @@ getJasmineRequireObj().Spy = function (j$) {
return Spy;
};
getJasmineRequireObj().SpyFactory = function(j$) {
function SpyFactory(getCustomStrategies) {
var self = this;
this.createSpy = function(name, originalFn) {
return j$.Spy(name, originalFn, getCustomStrategies());
};
this.createSpyObj = function(baseName, methodNames) {
var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName);
if (baseNameIsCollection && j$.util.isUndefined(methodNames)) {
methodNames = baseName;
baseName = 'unknown';
}
var obj = {};
var spiesWereSet = false;
if (j$.isArray_(methodNames)) {
for (var i = 0; i < methodNames.length; i++) {
obj[methodNames[i]] = self.createSpy(baseName + '.' + methodNames[i]);
spiesWereSet = true;
}
} else if (j$.isObject_(methodNames)) {
for (var key in methodNames) {
if (methodNames.hasOwnProperty(key)) {
obj[key] = self.createSpy(baseName + '.' + key);
obj[key].and.returnValue(methodNames[key]);
spiesWereSet = true;
}
}
}
if (!spiesWereSet) {
throw 'createSpyObj requires a non-empty array or object of method names to create spies for';
}
return obj;
};
}
return SpyFactory;
};
getJasmineRequireObj().SpyRegistry = function(j$) {
var getErrorMsg = j$.formatErrorMsg('<spyOn>', 'spyOn(<object>, <methodName>)');
@@ -5119,6 +5181,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
function SpyRegistry(options) {
options = options || {};
var global = options.global || j$.getGlobal();
var createSpy = options.createSpy;
var currentSpies = options.currentSpies || function() { return []; };
this.allowRespy = function(allow){
@@ -5154,7 +5217,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
}
var originalMethod = obj[methodName],
spiedMethod = j$.createSpy(methodName, originalMethod),
spiedMethod = createSpy(methodName, originalMethod),
restoreStrategy;
if (Object.prototype.hasOwnProperty.call(obj, methodName) || (obj === global && methodName === 'onerror')) {
@@ -5209,7 +5272,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
}
var originalDescriptor = j$.util.clone(descriptor),
spy = j$.createSpy(propertyName, descriptor[accessType]),
spy = createSpy(propertyName, descriptor[accessType]),
restoreStrategy;
if (Object.prototype.hasOwnProperty.call(obj, propertyName)) {
@@ -5263,6 +5326,26 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
this.originalFn = options.fn || function() {};
this.getSpy = options.getSpy || function() {};
this.plan = this._defaultPlan = function() {};
var k, cs = options.customStrategies || {};
for (k in cs) {
if (j$.util.has(cs, k) && !this[k]) {
this[k] = createCustomPlan(cs[k]);
}
}
}
function createCustomPlan(factory) {
return function() {
var plan = factory.apply(null, arguments);
if (!j$.isFunction_(plan)) {
throw new Error('Spy strategy must return a function');
}
this.plan = plan;
return this.getSpy();
};
}
/**

View File

@@ -255,12 +255,17 @@ describe("jasmineUnderTest.pp", function () {
},
env = new jasmineUnderTest.Env();
var spyRegistry = new jasmineUnderTest.SpyRegistry({currentSpies: function() {return [];}});
var spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {return [];},
createSpy: function(name, originalFn) {
return jasmineUnderTest.Spy(name, originalFn);
}
});
spyRegistry.spyOn(TestObject, 'someFunction');
expect(jasmineUnderTest.pp(TestObject.someFunction)).toEqual("spy on someFunction");
expect(jasmineUnderTest.pp(jasmineUnderTest.createSpy("something"))).toEqual("spy on something");
expect(jasmineUnderTest.pp(env.createSpy("something"))).toEqual("spy on something");
});
it("should stringify objects that implement jasmineToString", function () {

View File

@@ -1,7 +1,11 @@
describe("SpyRegistry", function() {
function createSpy(name, originalFn) {
return jasmineUnderTest.Spy(name, originalFn);
}
describe("#spyOn", function() {
it("checks for the existence of the object", function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry();
var spyRegistry = new jasmineUnderTest.SpyRegistry({createSpy: createSpy});
expect(function() {
spyRegistry.spyOn(void 0, 'pants');
}).toThrowError(/could not find an object/);
@@ -43,7 +47,10 @@ describe("SpyRegistry", function() {
it("checks if it has already been spied upon", function() {
var spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({currentSpies: function() { return spies; }}),
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() { return spies; },
createSpy: createSpy
}),
subject = { spiedFunc: function() {} };
spyRegistry.spyOn(subject, 'spiedFunc');
@@ -81,7 +88,7 @@ describe("SpyRegistry", function() {
it("overrides the method on the object and returns the spy", function() {
var originalFunctionWasCalled = false,
spyRegistry = new jasmineUnderTest.SpyRegistry(),
spyRegistry = new jasmineUnderTest.SpyRegistry({createSpy: createSpy}),
subject = { spiedFunc: function() { originalFunctionWasCalled = true; } };
var spy = spyRegistry.spyOn(subject, 'spiedFunc');
@@ -131,7 +138,7 @@ describe("SpyRegistry", function() {
});
it("checks if it has already been spied upon", function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry(),
var spyRegistry = new jasmineUnderTest.SpyRegistry({createSpy: createSpy}),
subject = {};
Object.defineProperty(subject, 'spiedProp', {
@@ -170,7 +177,7 @@ describe("SpyRegistry", function() {
});
it("overrides the property getter on the object and returns the spy", function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry(),
var spyRegistry = new jasmineUnderTest.SpyRegistry({createSpy: createSpy}),
subject = {},
returnValue = 1;
@@ -189,7 +196,7 @@ describe("SpyRegistry", function() {
});
it("overrides the property setter on the object and returns the spy", function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry(),
var spyRegistry = new jasmineUnderTest.SpyRegistry({createSpy: createSpy}),
subject = {},
returnValue = 1;
@@ -210,7 +217,10 @@ describe("SpyRegistry", function() {
describe("#clearSpies", function() {
it("restores the original functions on the spied-upon objects", function() {
var spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({currentSpies: function() { return spies; }}),
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() { return spies; },
createSpy: createSpy
}),
originalFunction = function() {},
subject = { spiedFunc: originalFunction };
@@ -222,7 +232,10 @@ describe("SpyRegistry", function() {
it("restores the original functions, even when that spy has been replace and re-spied upon", function() {
var spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({currentSpies: function() { return spies; }}),
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() { return spies; },
createSpy: createSpy
}),
originalFunction = function() {},
subject = { spiedFunc: originalFunction };
@@ -241,7 +254,10 @@ describe("SpyRegistry", function() {
it("does not add a property that the spied-upon object didn't originally have", function() {
var spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({currentSpies: function() { return spies; }}),
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() { return spies; },
createSpy: createSpy
}),
originalFunction = function() {},
subjectParent = {spiedFunc: originalFunction};
@@ -258,7 +274,10 @@ describe("SpyRegistry", function() {
it("restores the original function when it\'s inherited and cannot be deleted", function() {
var spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({currentSpies: function() { return spies; }}),
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() { return spies; },
createSpy: createSpy
}),
originalFunction = function() {},
subjectParent = {spiedFunc: originalFunction};
@@ -298,7 +317,10 @@ describe("SpyRegistry", function() {
describe('spying on properties', function() {
it("restores the original properties on the spied-upon objects", function() {
var spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({currentSpies: function() { return spies; }}),
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() { return spies; },
createSpy: createSpy
}),
originalReturn = 1,
subject = {};
@@ -315,7 +337,10 @@ describe("SpyRegistry", function() {
it("does not add a property that the spied-upon object didn't originally have", function() {
var spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({currentSpies: function() { return spies; }}),
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() { return spies; },
createSpy: createSpy
}),
originalReturn = 1,
subjectParent = {};

View File

@@ -1,4 +1,10 @@
describe('Spies', function () {
var env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
});
describe("createSpy", function() {
var TestClass;
@@ -9,7 +15,7 @@ describe('Spies', function () {
});
it("preserves the properties of the spied function", function() {
var spy = jasmineUnderTest.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
var spy = env.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
expect(spy.bob).toEqual("test");
});
@@ -18,19 +24,19 @@ describe('Spies', function () {
TestClass.prototype.someFunction.and = "turkey";
expect(function() {
jasmineUnderTest.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
env.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
}).toThrowError("Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon");
});
it("adds a spyStrategy and callTracker to the spy", function() {
var spy = jasmineUnderTest.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
var spy = env.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
expect(spy.and).toEqual(jasmine.any(jasmineUnderTest.SpyStrategy));
expect(spy.calls).toEqual(jasmine.any(jasmineUnderTest.CallTracker));
});
it("tracks the argument of calls", function () {
var spy = jasmineUnderTest.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
var spy = env.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
var trackSpy = spyOn(spy.calls, "track");
spy("arg");
@@ -39,7 +45,7 @@ describe('Spies', function () {
});
it("tracks the context of calls", function () {
var spy = jasmineUnderTest.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
var spy = env.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
var trackSpy = spyOn(spy.calls, "track");
var contextObject = { spyMethod: spy };
@@ -49,7 +55,7 @@ describe('Spies', function () {
});
it("tracks the return value of calls", function () {
var spy = jasmineUnderTest.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
var spy = env.createSpy(TestClass.prototype, TestClass.prototype.someFunction);
var trackSpy = spyOn(spy.calls, "track");
spy.and.returnValue("return value");
@@ -71,7 +77,7 @@ describe('Spies', function () {
for (var arity = 0; arity < functions.length; arity++) {
var someFunction = functions[arity],
spy = jasmineUnderTest.createSpy(someFunction.name, someFunction);
spy = env.createSpy(someFunction.name, someFunction);
expect(spy.length).toEqual(arity);
}
@@ -80,7 +86,7 @@ describe('Spies', function () {
describe("createSpyObj", function() {
it("should create an object with spy methods and corresponding return values when you call jasmine.createSpyObj() with an object", function () {
var spyObj = jasmineUnderTest.createSpyObj('BaseName', {'method1': 42, 'method2': 'special sauce' });
var spyObj = env.createSpyObj('BaseName', {'method1': 42, 'method2': 'special sauce' });
expect(spyObj.method1()).toEqual(42);
expect(spyObj.method1.and.identity).toEqual('BaseName.method1');
@@ -91,7 +97,7 @@ describe('Spies', function () {
it("should create an object with a bunch of spy methods when you call jasmine.createSpyObj()", function() {
var spyObj = jasmineUnderTest.createSpyObj('BaseName', ['method1', 'method2']);
var spyObj = env.createSpyObj('BaseName', ['method1', 'method2']);
expect(spyObj).toEqual({ method1: jasmine.any(Function), method2: jasmine.any(Function)});
expect(spyObj.method1.and.identity).toEqual('BaseName.method1');
@@ -99,7 +105,7 @@ describe('Spies', function () {
});
it("should allow you to omit the baseName", function() {
var spyObj = jasmineUnderTest.createSpyObj(['method1', 'method2']);
var spyObj = env.createSpyObj(['method1', 'method2']);
expect(spyObj).toEqual({ method1: jasmine.any(Function), method2: jasmine.any(Function)});
expect(spyObj.method1.and.identity).toEqual('unknown.method1');
@@ -108,25 +114,25 @@ describe('Spies', function () {
it("should throw if you do not pass an array or object argument", function() {
expect(function() {
jasmineUnderTest.createSpyObj('BaseName');
env.createSpyObj('BaseName');
}).toThrow("createSpyObj requires a non-empty array or object of method names to create spies for");
});
it("should throw if you pass an empty array argument", function() {
expect(function() {
jasmineUnderTest.createSpyObj('BaseName', []);
env.createSpyObj('BaseName', []);
}).toThrow("createSpyObj requires a non-empty array or object of method names to create spies for");
});
it("should throw if you pass an empty object argument", function() {
expect(function() {
jasmineUnderTest.createSpyObj('BaseName', {});
env.createSpyObj('BaseName', {});
}).toThrow("createSpyObj requires a non-empty array or object of method names to create spies for");
});
});
it("can use different strategies for different arguments", function() {
var spy = jasmineUnderTest.createSpy('foo');
var spy = env.createSpy('foo');
spy.and.returnValue(42);
spy.withArgs('baz', 'grault').and.returnValue(-1);
spy.withArgs('thud').and.returnValue('bob');
@@ -138,7 +144,7 @@ describe('Spies', function () {
});
it("uses custom equality testers when selecting a strategy", function() {
var spy = jasmineUnderTest.createSpy('foo');
var spy = env.createSpy('foo');
spy.and.returnValue(42);
spy.withArgs(jasmineUnderTest.any(String)).and.returnValue(-1);
@@ -147,7 +153,7 @@ describe('Spies', function () {
});
it("can reconfigure an argument-specific strategy", function() {
var spy = jasmineUnderTest.createSpy('foo');
var spy = env.createSpy('foo');
spy.withArgs('foo').and.returnValue(42);
spy.withArgs('foo').and.returnValue(17);
expect(spy('foo')).toEqual(17);
@@ -155,14 +161,14 @@ describe('Spies', function () {
describe("When withArgs is used without a base strategy", function() {
it("uses the matching strategy", function() {
var spy = jasmineUnderTest.createSpy('foo');
var spy = env.createSpy('foo');
spy.withArgs('baz').and.returnValue(-1);
expect(spy('baz')).toEqual(-1);
});
it("throws if the args don't match", function() {
var spy = jasmineUnderTest.createSpy('foo');
var spy = env.createSpy('foo');
spy.withArgs('bar').and.returnValue(-1);
expect(function() { spy('baz', {qux: 42}); }).toThrowError('Spy \'foo\' receieved a call with arguments [ \'baz\', Object({ qux: 42 }) ] but all configured strategies specify other arguments.');

View File

@@ -110,6 +110,49 @@ describe("SpyStrategy", function() {
})
});
it("allows a custom strategy to be used", function() {
var plan = jasmine.createSpy('custom strategy')
.and.returnValue('custom strategy result'),
customStrategy = jasmine.createSpy('custom strategy')
.and.returnValue(plan),
originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
fn: originalFn,
customStrategies: {
doSomething: customStrategy
}
});
spyStrategy.doSomething(1, 2, 3);
expect(customStrategy).toHaveBeenCalledWith(1, 2, 3);
expect(spyStrategy.exec(null, ['some', 'args']))
.toEqual('custom strategy result');
expect(plan).toHaveBeenCalledWith('some', 'args');
});
it("throws an error if a custom strategy doesn't return a function", function() {
var originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
fn: originalFn,
customStrategies: {
doSomething: function() { return 'not a function' }
}
});
expect(function() { spyStrategy.doSomething(1, 2, 3) }).toThrowError('Spy strategy must return a function');
});
it("does not allow custom strategies to overwrite existing methods", function() {
var spyStrategy = new jasmineUnderTest.SpyStrategy({
fn: function() {},
customStrategies: {
exec: function() {}
}
});
expect(spyStrategy.exec).toBe(jasmineUnderTest.SpyStrategy.prototype.exec);
});
it('throws an error when a non-function is passed to callFake strategy', function() {
var originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({fn: originalFn}),

View File

@@ -0,0 +1,138 @@
describe('Custom Spy Strategies (Integration)', function() {
var env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
env.randomizeTests(false);
});
it('allows adding more strategies local to a suite', function(done) {
var plan = jasmine.createSpy('custom strategy plan')
.and.returnValue(42);
var strategy = jasmine.createSpy('custom strategy')
.and.returnValue(plan);
env.describe('suite defining a custom spy strategy', function() {
env.beforeEach(function() {
env.addSpyStrategy('frobnicate', strategy);
});
env.it('spec in the suite', function() {
var spy = env.createSpy('something').and.frobnicate(17);
expect(spy(1, 2, 3)).toEqual(42);
expect(strategy).toHaveBeenCalledWith(17);
expect(plan).toHaveBeenCalledWith(1, 2, 3);
});
});
env.it('spec without custom strategy defined', function() {
expect(env.createSpy('something').and.frobnicate).toBeUndefined();
});
function jasmineDone(result) {
expect(result.overallStatus).toEqual('passed');
done();
}
env.addReporter({ jasmineDone: jasmineDone });
env.execute();
});
it('allows adding more strategies local to a spec', function(done) {
var plan = jasmine.createSpy('custom strategy plan')
.and.returnValue(42);
var strategy = jasmine.createSpy('custom strategy')
.and.returnValue(plan);
env.it('spec defining a custom spy strategy', function() {
env.addSpyStrategy('frobnicate', strategy);
var spy = env.createSpy('something').and.frobnicate(17);
expect(spy(1, 2, 3)).toEqual(42);
expect(strategy).toHaveBeenCalledWith(17);
expect(plan).toHaveBeenCalledWith(1, 2, 3);
});
env.it('spec without custom strategy defined', function() {
expect(env.createSpy('something').and.frobnicate).toBeUndefined();
});
function jasmineDone(result) {
expect(result.overallStatus).toEqual('passed');
done();
}
env.addReporter({ jasmineDone: jasmineDone });
env.execute();
});
it('allows using custom strategies on a per-argument basis', function(done) {
var plan = jasmine.createSpy('custom strategy plan')
.and.returnValue(42);
var strategy = jasmine.createSpy('custom strategy')
.and.returnValue(plan);
env.it('spec defining a custom spy strategy', function() {
env.addSpyStrategy('frobnicate', strategy);
var spy = env.createSpy('something')
.and.returnValue('no args return')
.withArgs(1, 2, 3).and.frobnicate(17);
expect(spy()).toEqual('no args return');
expect(plan).not.toHaveBeenCalled();
expect(spy(1, 2, 3)).toEqual(42);
expect(plan).toHaveBeenCalledWith(1, 2, 3);
});
env.it('spec without custom strategy defined', function() {
expect(env.createSpy('something').and.frobnicate).toBeUndefined();
});
function jasmineDone(result) {
expect(result.overallStatus).toEqual('passed');
done();
}
env.addReporter({ jasmineDone: jasmineDone });
env.execute();
});
it('allows multiple custom strategies to be used', function(done) {
var plan1 = jasmine.createSpy('plan 1').and.returnValue(42),
strategy1 = jasmine.createSpy('strat 1').and.returnValue(plan1),
plan2 = jasmine.createSpy('plan 2').and.returnValue(24),
strategy2 = jasmine.createSpy('strat 2').and.returnValue(plan2),
specDone = jasmine.createSpy('specDone');
env.beforeEach(function() {
env.addSpyStrategy('frobnicate', strategy1);
env.addSpyStrategy('jiggle', strategy2);
});
env.it('frobnicates', function() {
plan1.calls.reset();
plan2.calls.reset();
var spy = env.createSpy('spy').and.frobnicate();
expect(spy()).toEqual(42);
expect(plan1).toHaveBeenCalled();
expect(plan2).not.toHaveBeenCalled();
});
env.it('jiggles', function() {
plan1.calls.reset();
plan2.calls.reset();
var spy = env.createSpy('spy').and.jiggle();
expect(spy()).toEqual(24);
expect(plan1).not.toHaveBeenCalled();
expect(plan2).toHaveBeenCalled();
});
function jasmineDone(result) {
expect(result.overallStatus).toEqual('passed');
expect(specDone.calls.count()).toBe(2);
done();
}
env.addReporter({ jasmineDone: jasmineDone, specDone: specDone });
env.execute();
});
});

View File

@@ -2,14 +2,14 @@ describe("toHaveBeenCalledBefore", function() {
it("throws an exception when the actual is not a spy", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(),
fn = function() {},
secondSpy = jasmineUnderTest.createSpy('second spy');
secondSpy = new jasmineUnderTest.Env().createSpy('second spy');
expect(function() { matcher.compare(fn, secondSpy) }).toThrowError(Error, /Expected a spy, but got Function./);
});
it("throws an exception when the expected is not a spy", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(),
firstSpy = jasmineUnderTest.createSpy('first spy'),
firstSpy = new jasmineUnderTest.Env().createSpy('first spy'),
fn = function() {};
expect(function() { matcher.compare(firstSpy, fn) }).toThrowError(Error, /Expected a spy, but got Function./);
@@ -17,8 +17,8 @@ describe("toHaveBeenCalledBefore", function() {
it("fails when the actual was not called", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(),
firstSpy = jasmineUnderTest.createSpy('first spy'),
secondSpy = jasmineUnderTest.createSpy('second spy');
firstSpy = new jasmineUnderTest.Env().createSpy('first spy'),
secondSpy = new jasmineUnderTest.Env().createSpy('second spy');
secondSpy();
@@ -29,8 +29,8 @@ describe("toHaveBeenCalledBefore", function() {
it("fails when the expected was not called", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(),
firstSpy = jasmineUnderTest.createSpy('first spy'),
secondSpy = jasmineUnderTest.createSpy('second spy');
firstSpy = new jasmineUnderTest.Env().createSpy('first spy'),
secondSpy = new jasmineUnderTest.Env().createSpy('second spy');
firstSpy();
@@ -41,8 +41,8 @@ describe("toHaveBeenCalledBefore", function() {
it("fails when the actual is called after the expected", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(),
firstSpy = jasmineUnderTest.createSpy('first spy'),
secondSpy = jasmineUnderTest.createSpy('second spy'),
firstSpy = new jasmineUnderTest.Env().createSpy('first spy'),
secondSpy = new jasmineUnderTest.Env().createSpy('second spy'),
result;
secondSpy();
@@ -55,8 +55,8 @@ describe("toHaveBeenCalledBefore", function() {
it("fails when the actual is called before and after the expected", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(),
firstSpy = jasmineUnderTest.createSpy('first spy'),
secondSpy = jasmineUnderTest.createSpy('second spy'),
firstSpy = new jasmineUnderTest.Env().createSpy('first spy'),
secondSpy = new jasmineUnderTest.Env().createSpy('second spy'),
result;
firstSpy();
@@ -70,8 +70,8 @@ describe("toHaveBeenCalledBefore", function() {
it("fails when the expected is called before and after the actual", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(),
firstSpy = jasmineUnderTest.createSpy('first spy'),
secondSpy = jasmineUnderTest.createSpy('second spy'),
firstSpy = new jasmineUnderTest.Env().createSpy('first spy'),
secondSpy = new jasmineUnderTest.Env().createSpy('second spy'),
result;
secondSpy();
@@ -85,8 +85,8 @@ describe("toHaveBeenCalledBefore", function() {
it("passes when the actual is called before the expected", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(),
firstSpy = jasmineUnderTest.createSpy('first spy'),
secondSpy = jasmineUnderTest.createSpy('second spy'),
firstSpy = new jasmineUnderTest.Env().createSpy('first spy'),
secondSpy = new jasmineUnderTest.Env().createSpy('second spy'),
result;
firstSpy();

View File

@@ -1,7 +1,7 @@
describe("toHaveBeenCalled", function() {
it("passes when the actual was called, with a custom .not fail message", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalled(),
calledSpy = jasmineUnderTest.createSpy('called-spy'),
calledSpy = new jasmineUnderTest.Env().createSpy('called-spy'),
result;
calledSpy();
@@ -13,7 +13,7 @@ describe("toHaveBeenCalled", function() {
it("fails when the actual was not called", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalled(),
uncalledSpy = jasmineUnderTest.createSpy('uncalled spy'),
uncalledSpy = new jasmineUnderTest.Env().createSpy('uncalled spy'),
result;
result = matcher.compare(uncalledSpy);
@@ -29,14 +29,14 @@ describe("toHaveBeenCalled", function() {
it("throws an exception when invoked with any arguments", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalled(),
spy = jasmineUnderTest.createSpy('sample spy');
spy = new jasmineUnderTest.Env().createSpy('sample spy');
expect(function() { matcher.compare(spy, 'foo') }).toThrowError(Error, /Does not take arguments, use toHaveBeenCalledWith/);
});
it("has a custom message on failure", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalled(),
spy = jasmineUnderTest.createSpy('sample-spy'),
spy = new jasmineUnderTest.Env().createSpy('sample-spy'),
result;
result = matcher.compare(spy);

View File

@@ -1,14 +1,14 @@
describe("toHaveBeenCalledTimes", function() {
it("passes when the actual 0 matches the expected 0 ", function () {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(),
calledSpy = jasmineUnderTest.createSpy('called-spy'),
calledSpy = new jasmineUnderTest.Env().createSpy('called-spy'),
result;
result = matcher.compare(calledSpy, 0);
expect(result.pass).toBeTruthy();
});
it("passes when the actual matches the expected", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(),
calledSpy = jasmineUnderTest.createSpy('called-spy'),
calledSpy = new jasmineUnderTest.Env().createSpy('called-spy'),
result;
calledSpy();
@@ -18,7 +18,7 @@ describe("toHaveBeenCalledTimes", function() {
it("fails when expected numbers is not supplied", function(){
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(),
spy = jasmineUnderTest.createSpy('spy'),
spy = new jasmineUnderTest.Env().createSpy('spy'),
result;
spy();
@@ -29,7 +29,7 @@ describe("toHaveBeenCalledTimes", function() {
it("fails when the actual was called less than the expected", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(),
uncalledSpy = jasmineUnderTest.createSpy('uncalled spy'),
uncalledSpy = new jasmineUnderTest.Env().createSpy('uncalled spy'),
result;
result = matcher.compare(uncalledSpy, 2);
@@ -38,7 +38,7 @@ describe("toHaveBeenCalledTimes", function() {
it("fails when the actual was called more than expected", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(),
uncalledSpy = jasmineUnderTest.createSpy('uncalled spy'),
uncalledSpy = new jasmineUnderTest.Env().createSpy('uncalled spy'),
result;
uncalledSpy();
@@ -59,7 +59,7 @@ describe("toHaveBeenCalledTimes", function() {
it("has a custom message on failure that tells it was called only once", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(),
spy = jasmineUnderTest.createSpy('sample-spy'),
spy = new jasmineUnderTest.Env().createSpy('sample-spy'),
result;
spy();
spy();
@@ -72,7 +72,7 @@ describe("toHaveBeenCalledTimes", function() {
it("has a custom message on failure that tells how many times it was called", function() {
var matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(),
spy = jasmineUnderTest.createSpy('sample-spy'),
spy = new jasmineUnderTest.Env().createSpy('sample-spy'),
result;
spy();
spy();

View File

@@ -5,7 +5,7 @@ describe("toHaveBeenCalledWith", function() {
contains: jasmine.createSpy('delegated-contains').and.returnValue(true)
},
matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(util),
calledSpy = jasmineUnderTest.createSpy('called-spy'),
calledSpy = new jasmineUnderTest.Env().createSpy('called-spy'),
result;
calledSpy('a', 'b');
@@ -21,7 +21,7 @@ describe("toHaveBeenCalledWith", function() {
},
customEqualityTesters = [function() { return true; }],
matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(util, customEqualityTesters),
calledSpy = jasmineUnderTest.createSpy('called-spy');
calledSpy = new jasmineUnderTest.Env().createSpy('called-spy');
calledSpy('a', 'b');
matcher.compare(calledSpy, 'a', 'b');
@@ -34,7 +34,7 @@ describe("toHaveBeenCalledWith", function() {
contains: jasmine.createSpy('delegated-contains').and.returnValue(false)
},
matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(util),
uncalledSpy = jasmineUnderTest.createSpy('uncalled spy'),
uncalledSpy = new jasmineUnderTest.Env().createSpy('uncalled spy'),
result;
result = matcher.compare(uncalledSpy);
@@ -47,7 +47,7 @@ describe("toHaveBeenCalledWith", function() {
contains: jasmine.createSpy('delegated-contains').and.returnValue(false)
},
matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(util),
calledSpy = jasmineUnderTest.createSpy('called spy'),
calledSpy = new jasmineUnderTest.Env().createSpy('called spy'),
result;
calledSpy('a');

View File

@@ -108,6 +108,13 @@ getJasmineRequireObj().Env = function(j$) {
return true;
};
this.addSpyStrategy = function(name, fn) {
if(!currentRunnable()) {
throw new Error('Custom spy strategies must be added in a before function or a spec');
}
runnableResources[currentRunnable().id].customSpyStrategies[name] = fn;
};
this.addCustomEqualityTester = function(tester) {
if(!currentRunnable()) {
throw new Error('Custom Equalities must be added in a before function or a spec');
@@ -152,7 +159,7 @@ getJasmineRequireObj().Env = function(j$) {
};
var defaultResourcesForRunnable = function(id, parentRunnableId) {
var resources = {spies: [], customEqualityTesters: [], customMatchers: {}};
var resources = {spies: [], customEqualityTesters: [], customMatchers: {}, customSpyStrategies: {}};
if(runnableResources[parentRunnableId]){
resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters);
@@ -393,12 +400,27 @@ getJasmineRequireObj().Env = function(j$) {
reporter.clearReporters();
};
var spyRegistry = new j$.SpyRegistry({currentSpies: function() {
if(!currentRunnable()) {
throw new Error('Spies must be created in a before function or a spec');
var spyFactory = new j$.SpyFactory(function() {
var runnable = currentRunnable();
if (runnable) {
return runnableResources[runnable.id].customSpyStrategies;
}
return runnableResources[currentRunnable().id].spies;
}});
return {};
});
var spyRegistry = new j$.SpyRegistry({
currentSpies: function() {
if(!currentRunnable()) {
throw new Error('Spies must be created in a before function or a spec');
}
return runnableResources[currentRunnable().id].spies;
},
createSpy: function(name, originalFn) {
return self.createSpy(name, originalFn);
}
});
this.allowRespy = function(allow){
spyRegistry.allowRespy(allow);
@@ -412,6 +434,14 @@ getJasmineRequireObj().Env = function(j$) {
return spyRegistry.spyOnProperty.apply(spyRegistry, arguments);
};
this.createSpy = function(name, originalFn) {
return spyFactory.createSpy(name, originalFn);
};
this.createSpyObj = function(baseName, methodNames) {
return spyFactory.createSpyObj(baseName, methodNames);
};
var ensureIsFunction = function(fn, caller) {
if (!j$.isFunction_(fn)) {
throw new Error(caller + ' expects a function argument; received ' + j$.getType_(fn));

View File

@@ -13,7 +13,7 @@ getJasmineRequireObj().Spy = function (j$) {
* @constructor
* @name Spy
*/
function Spy(name, originalFn) {
function Spy(name, originalFn, customStrategies) {
var numArgs = (typeof originalFn === 'function' ? originalFn.length : 0),
wrapper = makeFunc(numArgs, function () {
return spy.apply(this, Array.prototype.slice.call(arguments));
@@ -23,7 +23,8 @@ getJasmineRequireObj().Spy = function (j$) {
fn: originalFn,
getSpy: function () {
return wrapper;
}
},
customStrategies: customStrategies
}),
callTracker = new j$.CallTracker(),
spy = function () {

45
src/core/SpyFactory.js Normal file
View File

@@ -0,0 +1,45 @@
getJasmineRequireObj().SpyFactory = function(j$) {
function SpyFactory(getCustomStrategies) {
var self = this;
this.createSpy = function(name, originalFn) {
return j$.Spy(name, originalFn, getCustomStrategies());
};
this.createSpyObj = function(baseName, methodNames) {
var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName);
if (baseNameIsCollection && j$.util.isUndefined(methodNames)) {
methodNames = baseName;
baseName = 'unknown';
}
var obj = {};
var spiesWereSet = false;
if (j$.isArray_(methodNames)) {
for (var i = 0; i < methodNames.length; i++) {
obj[methodNames[i]] = self.createSpy(baseName + '.' + methodNames[i]);
spiesWereSet = true;
}
} else if (j$.isObject_(methodNames)) {
for (var key in methodNames) {
if (methodNames.hasOwnProperty(key)) {
obj[key] = self.createSpy(baseName + '.' + key);
obj[key].and.returnValue(methodNames[key]);
spiesWereSet = true;
}
}
}
if (!spiesWereSet) {
throw 'createSpyObj requires a non-empty array or object of method names to create spies for';
}
return obj;
};
}
return SpyFactory;
};

View File

@@ -5,6 +5,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
function SpyRegistry(options) {
options = options || {};
var global = options.global || j$.getGlobal();
var createSpy = options.createSpy;
var currentSpies = options.currentSpies || function() { return []; };
this.allowRespy = function(allow){
@@ -40,7 +41,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
}
var originalMethod = obj[methodName],
spiedMethod = j$.createSpy(methodName, originalMethod),
spiedMethod = createSpy(methodName, originalMethod),
restoreStrategy;
if (Object.prototype.hasOwnProperty.call(obj, methodName) || (obj === global && methodName === 'onerror')) {
@@ -95,7 +96,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
}
var originalDescriptor = j$.util.clone(descriptor),
spy = j$.createSpy(propertyName, descriptor[accessType]),
spy = createSpy(propertyName, descriptor[accessType]),
restoreStrategy;
if (Object.prototype.hasOwnProperty.call(obj, propertyName)) {

View File

@@ -16,6 +16,26 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
this.originalFn = options.fn || function() {};
this.getSpy = options.getSpy || function() {};
this.plan = this._defaultPlan = function() {};
var k, cs = options.customStrategies || {};
for (k in cs) {
if (j$.util.has(cs, k) && !this[k]) {
this[k] = createCustomPlan(cs[k]);
}
}
}
function createCustomPlan(factory) {
return function() {
var plan = factory.apply(null, arguments);
if (!j$.isFunction_(plan)) {
throw new Error('Spy strategy must return a function');
}
this.plan = plan;
return this.getSpy();
};
}
/**

View File

@@ -173,18 +173,6 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return new j$.ArrayWithExactContents(sample);
};
/**
* Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it.
* @name jasmine.createSpy
* @function
* @param {String} [name] - Name to give the spy. This will be displayed in failure messages.
* @param {Function} [originalFn] - Function to act as the real implementation.
* @return {Spy}
*/
j$.createSpy = function(name, originalFn) {
return j$.Spy(name, originalFn);
};
j$.isSpy = function(putativeSpy) {
if (!putativeSpy) {
return false;
@@ -192,45 +180,4 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return putativeSpy.and instanceof j$.SpyStrategy &&
putativeSpy.calls instanceof j$.CallTracker;
};
/**
* Create an object with multiple {@link Spy}s as its members.
* @name jasmine.createSpyObj
* @function
* @param {String} [baseName] - Base name for the spies in the object.
* @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}.
* @return {Object}
*/
j$.createSpyObj = function(baseName, methodNames) {
var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName);
if (baseNameIsCollection && j$.util.isUndefined(methodNames)) {
methodNames = baseName;
baseName = 'unknown';
}
var obj = {};
var spiesWereSet = false;
if (j$.isArray_(methodNames)) {
for (var i = 0; i < methodNames.length; i++) {
obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]);
spiesWereSet = true;
}
} else if (j$.isObject_(methodNames)) {
for (var key in methodNames) {
if (methodNames.hasOwnProperty(key)) {
obj[key] = j$.createSpy(baseName + '.' + key);
obj[key].and.returnValue(methodNames[key]);
spiesWereSet = true;
}
}
}
if (!spiesWereSet) {
throw 'createSpyObj requires a non-empty array or object of method names to create spies for';
}
return obj;
};
};

View File

@@ -48,6 +48,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) {
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
j$.Spec = jRequire.Spec(j$);
j$.Spy = jRequire.Spy(j$);
j$.SpyFactory = jRequire.SpyFactory(j$);
j$.SpyRegistry = jRequire.SpyRegistry(j$);
j$.SpyStrategy = jRequire.SpyStrategy(j$);
j$.StringMatching = jRequire.StringMatching(j$);

View File

@@ -256,5 +256,42 @@ getJasmineRequireObj().interface = function(jasmine, env) {
return env.clock;
};
/**
* Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it.
* @name jasmine.createSpy
* @function
* @param {String} [name] - Name to give the spy. This will be displayed in failure messages.
* @param {Function} [originalFn] - Function to act as the real implementation.
* @return {Spy}
*/
jasmine.createSpy = function(name, originalFn) {
return env.createSpy(name, originalFn);
};
/**
* Create an object with multiple {@link Spy}s as its members.
* @name jasmine.createSpyObj
* @function
* @param {String} [baseName] - Base name for the spies in the object.
* @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}.
* @return {Object}
*/
jasmine.createSpyObj = function(baseName, methodNames) {
return env.createSpyObj(baseName, methodNames);
};
/**
* Add a custom spy strategy for the current scope of specs.
*
* _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}.
* @name jasmine.addSpyStrategy
* @function
* @param {String} name - The name of the strategy (i.e. what you call from `and`)
* @param {Function} factory - Factory function that returns the plan to be executed.
*/
jasmine.addSpyStrategy = function(name, factory) {
return env.addSpyStrategy(identifier, factory);
};
return jasmineInterface;
};