Files
jasmine/spec/core/SpySpec.js
Steve Gravrock 434575f49d Use one declaration per statement
The old style of merging all of a function's variable declarations into
a single statement made some sense back in the days of var, but there's
no reason to keep doing it now that we use const and let.
2026-03-11 06:30:46 -07:00

299 lines
9.4 KiB
JavaScript

describe('Spies', function() {
let env;
beforeEach(function() {
env = new privateUnderTest.Env();
});
afterEach(function() {
env.cleanup_();
});
describe('createSpy', function() {
let TestClass;
beforeEach(function() {
TestClass = function() {};
TestClass.prototype.someFunction = function() {};
TestClass.prototype.someFunction.bob = 'test';
});
it('preserves the properties of the spied function', function() {
const spy = env.createSpy(
TestClass.prototype,
TestClass.prototype.someFunction
);
expect(spy.bob).toEqual('test');
});
it('should allow you to omit the name argument and only pass the originalFn argument', function() {
const fn = function test() {};
const spy = env.createSpy(fn);
expect(spy.and.identity).toEqual('test');
});
it('warns the user that we intend to overwrite an existing property', function() {
TestClass.prototype.someFunction.and = 'turkey';
expect(function() {
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() {
const spy = env.createSpy(
TestClass.prototype,
TestClass.prototype.someFunction
);
expect(spy.and).toEqual(jasmine.any(privateUnderTest.SpyStrategy));
expect(spy.calls).toEqual(jasmine.any(privateUnderTest.CallTracker));
});
it('tracks the argument of calls', function() {
const spy = env.createSpy(
TestClass.prototype,
TestClass.prototype.someFunction
);
const trackSpy = spyOn(spy.calls, 'track');
spy('arg');
expect(trackSpy.calls.mostRecent().args[0].args).toEqual(['arg']);
});
it('tracks the context of calls', function() {
const spy = env.createSpy(
TestClass.prototype,
TestClass.prototype.someFunction
);
const trackSpy = spyOn(spy.calls, 'track');
const contextObject = { spyMethod: spy };
contextObject.spyMethod();
expect(trackSpy.calls.mostRecent().args[0].object).toEqual(contextObject);
});
it('tracks the return value of calls', function() {
const spy = env.createSpy(
TestClass.prototype,
TestClass.prototype.someFunction
);
const trackSpy = spyOn(spy.calls, 'track');
spy.and.returnValue('return value');
spy();
expect(trackSpy.calls.mostRecent().args[0].returnValue).toEqual(
'return value'
);
});
it('preserves arity of original function', function() {
const functions = [
function nullary() {},
function unary(arg) {},
function binary(arg1, arg2) {},
function ternary(arg1, arg2, arg3) {},
function quaternary(arg1, arg2, arg3, arg4) {},
function quinary(arg1, arg2, arg3, arg4, arg5) {},
function senary(arg1, arg2, arg3, arg4, arg5, arg6) {}
];
for (let arity = 0; arity < functions.length; arity++) {
const someFunction = functions[arity];
const spy = env.createSpy(someFunction.name, someFunction);
expect(spy.length).toEqual(arity);
}
});
});
describe('createSpyObj', function() {
it('should create an object with spy methods and corresponding return values when you call jasmine.createSpyObj() with an object', function() {
const spyObj = env.createSpyObj('BaseName', {
method1: 42,
method2: 'special sauce'
});
expect(spyObj.method1()).toEqual(42);
expect(spyObj.method1.and.identity).toEqual('BaseName.method1');
expect(spyObj.method2()).toEqual('special sauce');
expect(spyObj.method2.and.identity).toEqual('BaseName.method2');
});
it('should create an object with a bunch of spy methods when you call jasmine.createSpyObj()', function() {
const 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');
expect(spyObj.method2.and.identity).toEqual('BaseName.method2');
});
it('should allow you to omit the baseName', function() {
const spyObj = env.createSpyObj(['method1', 'method2']);
expect(spyObj).toEqual({
method1: jasmine.any(Function),
method2: jasmine.any(Function)
});
expect(spyObj.method1.and.identity).toEqual('unknown.method1');
expect(spyObj.method2.and.identity).toEqual('unknown.method2');
});
it('should throw if you do not pass an array or object argument', function() {
expect(function() {
env.createSpyObj('BaseName');
}).toThrowError(
'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() {
env.createSpyObj('BaseName', []);
}).toThrowError(
'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() {
env.createSpyObj('BaseName', {});
}).toThrowError(
'createSpyObj requires a non-empty array or object of method names to create spies for'
);
});
it('creates an object with spy properties if a second list is passed', function() {
const spyObj = env.createSpyObj('base', ['method1'], ['prop1']);
expect(spyObj).toEqual({
method1: jasmine.any(Function),
prop1: undefined
});
const descriptor = Object.getOwnPropertyDescriptor(spyObj, 'prop1');
expect(descriptor.get.and.identity).toEqual('base.prop1.get');
expect(descriptor.set.and.identity).toEqual('base.prop1.set');
});
it('creates an object with property names and return values if second object is passed', function() {
const spyObj = env.createSpyObj('base', ['method1'], {
prop1: 'foo',
prop2: 37
});
expect(spyObj).toEqual({
method1: jasmine.any(Function),
prop1: 'foo',
prop2: 37
});
expect(spyObj.prop1).toEqual('foo');
expect(spyObj.prop2).toEqual(37);
spyObj.prop2 = 4;
expect(spyObj.prop2).toEqual(37);
expect(
Object.getOwnPropertyDescriptor(spyObj, 'prop2').set.calls.count()
).toBe(1);
});
it('allows base name to be omitted when assigning methods and properties', function() {
const spyObj = env.createSpyObj({ m: 3 }, { p: 4 });
expect(spyObj.m()).toEqual(3);
expect(spyObj.p).toEqual(4);
expect(
Object.getOwnPropertyDescriptor(spyObj, 'p').get.and.identity
).toEqual('unknown.p.get');
});
});
it('can use different strategies for different arguments', function() {
const spy = env.createSpy('foo');
spy.and.returnValue(42);
spy.withArgs('baz', 'grault').and.returnValue(-1);
spy.withArgs('thud').and.returnValue('bob');
expect(spy('foo')).toEqual(42);
expect(spy('baz', 'grault')).toEqual(-1);
expect(spy('thud')).toEqual('bob');
expect(spy('baz', 'grault', 'waldo')).toEqual(42);
});
it('uses asymmetric equality testers when selecting a strategy', function() {
const spy = env.createSpy('foo');
spy.and.returnValue(42);
spy.withArgs(jasmineUnderTest.any(String)).and.returnValue(-1);
expect(spy('foo')).toEqual(-1);
expect(spy({})).toEqual(42);
});
it('uses the provided matchersUtil selecting a strategy', function() {
const matchersUtil = new privateUnderTest.MatchersUtil({
customTesters: [
function(a, b) {
if ((a === 'bar' && b === 'baz') || (a === 'baz' && b === 'bar')) {
return true;
}
}
]
});
const spy = new privateUnderTest.Spy('aSpy', matchersUtil);
spy.and.returnValue('default strategy return value');
spy.withArgs('bar').and.returnValue('custom strategy return value');
expect(spy('foo')).toEqual('default strategy return value');
expect(spy('baz')).toEqual('custom strategy return value');
});
it('can reconfigure an argument-specific strategy', function() {
const spy = env.createSpy('foo');
spy.withArgs('foo').and.returnValue(42);
spy.withArgs('foo').and.returnValue(17);
expect(spy('foo')).toEqual(17);
});
describe('any promise-based strategy', function() {
it('works with global Promise library', function(done) {
const spy = env.createSpy('foo').and.resolveTo(42);
spy()
.then(function(result) {
expect(result).toEqual(42);
done();
})
.catch(done.fail);
});
});
describe('when withArgs is used without a base strategy', function() {
it('uses the matching strategy', function() {
const spy = env.createSpy('foo');
spy.withArgs('baz').and.returnValue(-1);
expect(spy('baz')).toEqual(-1);
});
it("throws if the args don't match", function() {
const spy = env.createSpy('foo');
spy.withArgs('bar').and.returnValue(-1);
expect(function() {
spy('baz', { qux: 42 });
}).toThrowError(
"Spy 'foo' received a call with arguments [ 'baz', Object({ qux: 42 }) ] but all configured strategies specify other arguments."
);
});
});
});