This post and the examples have been updated to the latest release of Jasmine, which is currently 3.5.

Jasmine is a simple, BDD-style JavaScript testing framework, but to benefit from the full power out of the framework, you need to know how to mock calls the Jasmine way.

Jasmine uses spies to mock asynchronous and synchronous function calls. As with most mocking frameworks, you can set the externally observed behavior of the code you are mocking.

Using Jasmine spies to mock code

Jasmine spies are easy to set up.  You set the object and function you want to spy on, and that code won't be executed.

In the code below, we have a MyApp module with a flag property and a setFlag() function exposed.  We also have an instance of that module called myApp in the test.  To spy on the myApp.setFlag() function, we use:

spyOn(myApp, "setFlag");

It's a little strange that in Jasmine, you have to put the function you want to spy on in quotes, but that's the API.

When you spy on a function like this, the original code is not executed.

// Code under test
var MyApp = function () {
    var testAsync = function () {
        // Do something that takes a long time...
    };

    var callSomethingThatUsesAsync = function () {
        // Get a jQuery deferred
        var deferred = $.Deferred();

        // Make an async call and do something when it's completed based on success/failure
        this.testAsync()
        .done(function () {
            // Resolve the deferred
            deferred.resolve("The async call succeeded");
        })
        .fail(function () {
            // Reject the deferred
            deferred.reject("The async call failed");
        });

        return deferred.promise();
    };

    return {
        testAsync: testAsync,
        callSomethingThatUsesAsync: callSomethingThatUsesAsync
    };
};

// Test helpers
var JasmineHelpers = function () {

    var deferredSuccess = function (args) {
        var d = $.Deferred();
        d.resolve(args);
        return d.promise();
    };

    var deferredFailure = function (args) {
        var d = $.Deferred();
        d.reject(args);
        return d.promise();
    };

    return {
        deferredSuccess: deferredSuccess,
        deferredFailure: deferredFailure
    };
};

// Specs
describe("Testing spies", function () {
    var jasmineHelpers = new JasmineHelpers();

    it("Should spy on async calls and use helpers to mock success/failure", function () {
        // Arrange
        var myApp = new MyApp();
        spyOn(myApp, "testAsync").and.callFake(function () {
            return jasmineHelpers.deferredSuccess();
        });

        // Act
        myApp.callSomethingThatUsesAsync()
        .always(function (result) {
            // Assert
            expect(result).toEqual("The async call succeeded");
        });
    });

});

Returning values from Jasmine spies

Most of the time when setting up mocks, you want to set return values so you can test a specific code path. Again, this is easy to do with Jasmine.

Here, I show setting the return value of a function so we can test specific branches in the code and skip over the real getFlag() function, which is hard-coded to return false.

// Code under test
var MyApp = function () {
    var getFlag = function () {
        return false;
    };

    var useFlagForSomething = function () {
        if (this.getFlag() === true) {
            return "It was true";
        } else {
            return "It was false";
        }
    };

    return {
        getFlag: getFlag,
        useFlagForSomething: useFlagForSomething
    };
};

// Specs
describe("Testing spies", function () {

    it("Should replace the return value of function being spied on", function () {
        // Arrange
        var myApp = new MyApp();
        spyOn(myApp, "getFlag").and.returnValue(true);

        // Act
        var result = myApp.useFlagForSomething();

        // Assert
        expect(result).toEqual("It was true");
    });

});

The key piece is intercepting the getFlag() function with the spy and setting the value the substituted function returns:

spyOn(myApp, "getFlag").and.returnValue(true);

Sometimes, setting a returnValue isn't enough. If you need to replace the function you are mocking, you can use:

spyOn(myApp, "useFlagForSomething").and.callFake(function() {
    return "I'm replacing the real function with this";
});

Using Jasmine spies to verify code was called

You can also call the original code with a spy. This may sound pointless (why set up a mock and then call the real code?) unless you think about interaction testing, where it's important to know that a function was or was not called.

The syntax to call the original code but still spy on the function is:

spyOn(myApp, "getFlag").and.callThrough();

The code below shows how you can verify a spied on function was called. We have a new function called reallyImportantProcess().  We have to test to make sure it's being run under the right conditions, and we know it should run when getFlag() returns false, which is the default value. So we don't need to mock or change getFalse() to take this code branch, but we do need to spyOn reallyImportantProcess() to verify it gets called.

In this spy, we have lots of options. We could skip calling the real code, as we do below, or we could set up specific return values or substitute our own function for that function, or we could call the original code with callThrough(). When you set up Jasmine spies, you can use any spy configuration and still see if it was called later with and toHaveBeenCalled().

// Code under test
var MyApp = function () {
    var getFlag = function () {
        return false;
    };

    var reallyImporatantProcess = function () {
        // Do something really important
    };

    var useFlagForSomething = function () {
        if (this.getFlag() === true) {
            return "It was true";
        } else {
            this.reallyImporatantProcess();
            return "It was false";
        }
    };

    return {
        getFlag: getFlag,
        reallyImporatantProcess: reallyImporatantProcess,
        useFlagForSomething: useFlagForSomething
    };
};

// Specs
describe("Testing spies", function () {

    it("Should verify a spy was called", function () {
        // Arrange
        var myApp = new MyApp();
        spyOn(myApp, "reallyImporatantProcess");

        // Act
        var result = myApp.useFlagForSomething();

        // Assert
        expect(myApp.reallyImporatantProcess).toHaveBeenCalled();
        expect(result).toEqual("It was false");
    });

});

You can also test that a spied on function was NOT called with:

expect(myApp.reallyImportantProcess).not.toHaveBeenCalled();

Or you can go further with your interaction testing to assert on the spied on function being called with specific arguments like:

expect(myApp.reallyImportantProcess).toHaveBeenCalledWith(123, "abc");

Testing async calls in Jasmine with jQuery's Deferred and helpers

Async calls are a big part of JavaScript.  Most code dealing with async calls these day works through promises or callbacks.

Here, some test helper code can be used to mock promises being returned from an async call. I'm using jQuery's $.Deferred() object, but any promise framework should work the same.

You can mock an async success or failure, pass in anything you want the mocked async call to return, and test how your code handles it:

var JasmineHelpers = function () {

    var deferredSuccess = function (args) {
        var d = $.Deferred();
        d.resolve(args);
        return d.promise();
    };

    var deferredFailure = function (args) {
        var d = $.Deferred();
        d.reject(args);
        return d.promise();
    };

    return {
        deferredSuccess: deferredSuccess,
        deferredFailure: deferredFailure
    };
};

Here is some Jasmine spy code using these async helpers. Again, we use jQuery $.Deferred() object to set up a function that calls out to a pretend async call named testAsync().  It's invoked by callSomethingThatUsesAsync(), which is the function we're testing.  We want to mock the testAsync() call (maybe it goes off to the database and takes a while), and we want to assert that when that testAsync() call succeeds, callSomethingThatUsesAsync() goes on to give us a text message saying it succeeded.

Since the function under test is using a promise, we need to wait until the promise has completed before we can start asserting, but we want to assert whether it fails or succeeds, so we'll put our assert code in the always() function.  The done(), fail(), and always() functions are promise code - (actually, jQuery $.Deferred code if you want to be technical) they are not specific to Jasmine.

Testing async calls in Jasmine with Jasmine's done() callback

Testing synchronous specs is easy, but asynchronous testing requires some additional work.

For example, the code below fails because Jasmine evaluates the expect() piece before the testAsync() function has finished its work.  The setTimeout() call forces a two second delay, but Jasmine has already moved on and failed the test before the setTimeout() completes:

// Code under test
var flag = false;

function testAsync(done) {
    // Wait two seconds, then set the flag to true
    setTimeout(function () {
        flag = true;
    }, 2000);
}

// Specs
describe("Testing async calls", function () {
    
    it("Should be true if the async call has completed", function () {
        expect(flag).toEqual(true);
    });

});

With Jasmine async testing, we have to call the async code in the beforeEach() function that runs before each it() function block within a describe() function block.

We also have to let Jasmine know when the async function has completed by calling the special done() callback function Jasmine provides. Here, we are passing this special done() callback around so our code under test can invoke it.

// Code under test
var flag = false;

function testAsync(done) {
    // Wait two seconds, then set the flag to true
    setTimeout(function () {
        flag = true;

        // Invoke the special done callback
        done();
    }, 2000);
}

// Specs
describe("Testing async calls with beforeEach and passing the special done callback around", function () {

    beforeEach(function (done) {
        // Make an async call, passing the special done callback        
        testAsync(done);
    });

    it("Should be true if the async call has completed", function () {
        expect(flag).toEqual(true);
    });

});

This works, but changing the code under test to accept the done() callback argument and invoke it may not be realistic. Why would you change your code under test just to make the testing framework happy?

Instead, you can use promises and call the special Jasmine done() callback when your promise has resolved. Note that the Jasmine done() callback is NOT the same as the promise's done() callback. They just use the same function name.

Here, I'm using jQuery's $.Deferred() object for the promises, but this approach should work with any promises library.

// Code under test
var flag = false;

function testAsyncWithDeferred() {
    // Get a jQuery deferred
    var deferred = $.Deferred();

    // Wait two seconds, then set the flag to true
    setTimeout(function () {
        flag = true;

        // Resolve the deferred
        deferred.resolve();
    }, 2000);

    // Return the deferred promise
    return deferred.promise();
}

// Specs
describe("Testing async calls with beforeEach and invoking the special done callback in the promise's done callback", function () {

    beforeEach(function(done) {
        testAsyncWithDeferred()
        .done(function (result) {
            // Invoke the special done callback
            done();
        });
    });
               
    it("Should be true if the async call has completed", function () {
        expect(flag).toEqual(true);
    });

});

You can even use the data returned from the promise in the test once it is resolved. Here we are passing the return value in the deferred.resolve() call:

// Code under test
function testAsyncWithDeferredReturnValue() {
    // Get a jQuery deferred
    var deferred = $.Deferred();

    // Wait two seconds, then set the return true
    setTimeout(function () {
        // Resolve the deferred
        deferred.resolve(true);
    }, 2000);

    // Return the deferred promise
    return deferred.promise();
}

// Specs
describe("Testing async calls with beforeEach and invoking the special done callback in the promise's done callback and using the promise's return data", function () {
    var flag = false;

    beforeEach(function (done) {
        testAsyncWithDeferredReturnValue()
        .done(function (result) {
            flag = result;
            
            // Invoke the special done callback
            done();
        });
    });
    
    it("Should be true if the async call has completed", function () {
        expect(flag).toEqual(true);
    });

});

But of course, real-world applications can't get away with simply testing with setTimeout and true/false flags, so here is a more real-world example.

It calls $.getJSON() to go fetch some public JSON data in the beforeEach() function, and then tests the returned JSON in the it() block to make sure it isn't an empty object or undefined.

// Code under test
function testAsyncRealWorld() {
    // Get a jQuery deferred
    var deferred = $.Deferred();
    
    // Real world data call
    var data = $.getJSON("https://data.colorado.gov/resource/4ykn-tg5h.json", function() {
    	deferred.resolve(true);
    });
    
    // Return the deferred promise
    return deferred.promise();
}

// Specs
describe("Testing async calls with real-world data", function () {
    var data = null;

    beforeEach(function (done) {
        testAsyncRealWorld()
        .done(function (result) {
            data = result;
            
            // Invoke the special done callback
            done();
        });
    });
    
    it("Should have result if the async call has completed", function () {
        expect(data).not.toBeNull();
    });

});

Testing async calls in Jasmine with async/await

With version 2.8 and later of Jasmine and your compiler that supports async/await (e.g., Babel, TypeScript), you can change this to be more readable:

describe("Testing async functions", () => {
  
  it("Should work with async/await", async () => {
    // Arrange
    let flag = false;

    // Act
    flag = await returnTrueAsync();

    // Assert
    expect(flag).toBeTruthy();
  }); 
  
});

function returnTrueAsync() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(true);
    }, 1000);
  });
}

Much cleaner!