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.

jasmine createspy

createSpy() takes two parameters, both of which are optional:

				
				jasmine.createSpy(name, originalFunction)
				
			
name
Optional. The name of the spy.
originalFunction
Optional. The function which is to behave like the real implementation.

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.

jasmine

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!');
						    });
						});
					
				
jasmine createspy returnvalue

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!');
						    });
						});
					
				
jasmine createspy callfake

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
Optional. Common name for the spies.
methodNames
Required. The names of the spy methods as an array of strings or as the properties of the object whose values are their respective return values.

We can forget our usual example constructor function here for the time being as all spy methods below are created out of nothing.

jasmine createspyobj
					
					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();
					    });
					});
					
				
jasmine createspyobj methods

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();
					    });
					});