299 lines
9.4 KiB
JavaScript
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],
|
|
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."
|
|
);
|
|
});
|
|
});
|
|
});
|