Jasmine: createSpy() and createSpyObj()
Jasmine's
createSpy()
method is useful when you do not have any function to spy upon or when
the call to the original function would inflict a lag in time
(especially if it involves HTTP requests) or has other dependencies
which may not be available in the current context.
Like
spyOn(), it also tracks calls and arguments, but without any real
implementation. But unlike
spyOn(), the method to be spied upon need not exist at all.
createSpy() takes two parameters, both of
which are optional:
jasmine.createSpy(name, originalFunction)
name
originalFunction
Now consider the constructor function
Bird() below. The constructor has a method
called getSound() which returns the text
'Chirp, Chirp!'.
function Bird() {
this.getSound = function() {
return 'Chirp, Chirp!';
}
};
createSpy()
We create an instance of Bird(), which
is b. This instance inherits the
getSound() method from the constructor,
but we have replaced it with a spy returning fake method using
jasmine.createSpy() below.
describe("Testing Bird with fake getSound() method", function() {
it('calls the fake getSound() method created by createSpy()', function() {
var b = new Bird();
b.getSound = jasmine.createSpy();
b.getSound();
expect(b.getSound).toHaveBeenCalled();
});
});
The above spec passes.
However, it is always a good practice to pass at least the method
name to createSpy() so as to identify
the function if some errors toss up while running the spec. Else,
messages as the following would get printed in the terminal.
'undefined' is not a function (evaluating 'jasmine.createSpy() ...
The toHaveBeenCalled() matcher used
above may not have been that transparent, in the sense that it might
have left you wondering whether it was the real method belonging to
the constructor that got invoked or the fake one returned by
createSpy(). So let us clear things up
by returning some specific values we can distinguish. We make use of
the and.returnValue() method for the
purpose. The return text is changed to
'Tweet, Tweet!'.
The console.log() statement was actually
quite unnecessary, but it was just to bring to notice in your
terminal that the original "Chirp, Chirp!" has really been replaced
with the value returned by a fakie. Note that
getSound() is invoked inside the
expect() function; it is
getSound() and not
getSound.
describe("Testing Bird with fake getSound() method", function() {
it('calls the fake getSound() created by createSpy() with return value', function() {
var b = new Bird();
b.getSound = jasmine.createSpy().and.returnValue('Tweet, Tweet!');
console.log(b.getSound());
expect(b.getSound()).toEqual('Tweet, Tweet!');
});
});
Actually, the entire function can be replicated using
callFake().
describe("Testing Bird with fake getSound() method", function() {
it('calls the fake getSound() method created by createSpy()', function() {
var b = new Bird();
b.getSound = jasmine.createSpy().and.callFake(function(){
return 'Hoot, Hoot!';
});
console.log(b.getSound());
expect(b.getSound()).toEqual('Hoot, Hoot!');
});
});
createSpyObj()
The
createSpyObj()
creates a mock object with multiple spies. The created object has
the spy methods as its properties, with their respective return
values as its values. The syntax is as follows:
jasmine.createSpyObj(baseName, methodNames)
baseName
methodNames
We can forget our usual example constructor function here for the time being as all spy methods below are created out of nothing.
describe("Multiple spies created with createSpyObj()", function() {
var ball;
beforeEach(function() {
ball = jasmine.createSpyObj('ball', ['roll', 'bounce', 'stop']);
});
it('should create multiple spy methods', function() {
expect(ball.roll).toBeDefined();
expect(ball.bounce).toBeDefined();
expect(ball.stop).toBeDefined();
});
});
Next we check if they have been invoked, with the
toHaveBeenCalled() matcher.
describe("Multiple spies created with createSpyObj()", function() {
var ball;
beforeEach(function() {
ball = jasmine.createSpyObj('ball', ['roll', 'bounce', 'stop']);
ball.roll();
ball.bounce(4);
ball.stop();
});
it('should track the invoked spy methods', function() {
expect(ball.roll).toHaveBeenCalled();
expect(ball.bounce).toHaveBeenCalled();
expect(ball.stop).toHaveBeenCalled();
});
});