Jemmitch
Jemmitch

Reputation: 185

Jasmine test for an ajax request that returns a promise

I am new to Angular testing.

I am trying to test a simple method in a service that gets some data via an ajax call and returns a promise using Jasmine.

So far very unsuccessfully.

This is the method I am testing:

function getDefaultFibonnacci() {
        var deferred = $q.defer();

        $http({ method: 'GET', url: '/api/fibonnacci/get' })
            .success(function (data) {
                deferred.resolve(data);
            })
            .error(function (data, status) {
                deferred.reject(status);
            });

        return deferred.promise;
    }

This is my test code: (Please note all other tests pass apart from 'should return 0,1,1,2,3'

describe('datacontext', function () {
    var $httpBackend;
    var $rootScope;
    var datacontext;
    var $q;

    beforeEach(function () {
        module('app');
        inject(function (_$httpBackend_, _$rootScope_, _$q_, _datacontext_) {
            $httpBackend = _$httpBackend_;
            $rootScope = _$rootScope_;
            datacontext = _datacontext_;
            $q = _$q_;
        });
    });

    it('should have a getDefaultFibonnacci() function', function () {
        expect(angular.isFunction(datacontext.getDefaultFibonnacci)).toBe(true);
    });

    it('should return a promise', function () {
        expect(datacontext.getDefaultFibonnacci().then).toBeDefined();
    });

    it('should return 0,1,1,2,3', function () {
        var sequence = '123';

        $httpBackend.when('GET', 'app/dashboard/dashboard.html').respond('');
        $httpBackend.when('GET', '/api/fibonnacci/get').respond('0,1,1,2,3');

        var deferred = $q.defer();
        var promise = deferred.promise;

        promise.then(function (response) {
            sequence = response.success;
        });

        datacontext.getDefaultFibonnacci().then(function (data) { deferred.resolve(data); });

        $rootScope.$digest();

        expect(sequence).toEqual('0,1,1,2,3');
    });
});

Guys thanks for all your comments. I learnt a lot through this exercise.

This is the code I ended up with for a passing test.

function getDefaultFibonnacci() {
        return $http({ method: 'GET', url: '/api/fibonnacci/get' });
    }

it('should return 0,1,1,2,3', function () {
        var sequence;

        $httpBackend.whenGET('app/dashboard/dashboard.html').respond('');

        $httpBackend.expectGET('/api/fibonnacci/get').respond('0,1,1,2,3');

        datacontext.getDefaultFibonnacci().then(function (data) {
            sequence = data.data;
        });

        $httpBackend.flush();

        expect(sequence).toEqual('0,1,1,2,3');
    });

Upvotes: 3

Views: 2509

Answers (3)

Ed_
Ed_

Reputation: 19098

$httpBackend has a flush() method for exactly this reason.

flush() simulates the http server responding, so it will trigger the resolution of your $http.get(). Until you call flush(), nothing will happen (the server hasn't responded yet).

As such, replace your $rootScope.digest() code with $httpBackend.flush() and work from there.

Furthermore, you can save a lot of effort by understanding that $http methods themselves return promises.

This:

function getDefaultFibonnacci() {
    var deferred = $q.defer();

    $http({ method: 'GET', url: '/api/fibonnacci/get' })
        .success(function (data) {
            deferred.resolve(data);
        })
        .error(function (data, status) {
            deferred.reject(status);
        });

    return deferred.promise;
}

Can be simplified to this:

function getDefaultFibonnacci() {

    return $http({ method: 'GET', url: '/api/fibonnacci/get' })

}

And will do the same thing.

Finally you don't need another promise in your test. This is enough (remove all reference to $q and deferred and put this directly after your $httpBackend.when(... code):

datacontext.getDefaultFibonnacci()
.then(function (data) { 
    sequence = data;
});

Upvotes: 4

Remco Haszing
Remco Haszing

Reputation: 7809

The most important think you forgot to do is call $httpBackend.flush() at some point after making the requests before using the data.

Also you don't need to create an extra promise.

it('should return 0,1,1,2,3', function () {
    var sequence;

    // Use the shorthand method whenGET
    $httpBackend.whenGET('app/dashboard/dashboard.html').respond('');

    // We should probably test that this request is actually made, so use expect<method>
    $httpBackend.expectGET('/api/fibonnacci/get').respond('0,1,1,2,3');

    // Leave out the extra promise code.
    // var deferred = $q.defer();
    // var promise = deferred.promise;

    // promise.then(function (response) {
    //     sequence = response.success;
    // });

    // datacontext.getDefaultFibonnacci().then(function (data) { deferred.resolve(data); });

    datacontext.getDefaultFibonnacci().then(function (response) {
        sequence = response.success;
    });

    $httpBackend.flush(); // Flush the backend. Important!
    // $rootScope.$digest(); // I don't think this is necessary.

    expect(sequence).toEqual('0,1,1,2,3');
});

The html template won't be called if you set up your app

Upvotes: 1

Prasad K - Google
Prasad K - Google

Reputation: 2584

Are you expecting an object {success: "0,1,1,2,3"} as the response from the http service? You're using response.success when promise resolved

promise.then(function (response) {
            sequence = response.success;
        });

whereas you're returning a string '0,1,1,2,3'

$httpBackend.when('GET', '/api/fibonnacci/get').respond('0,1,1,2,3');

Also, from the code I see that you don't need to create another promise to test your method.

Try this:

it('should return 0,1,1,2,3', function () {
        var sequence = '123';

        $httpBackend.when('GET', 'app/dashboard/dashboard.html').respond('');
        $httpBackend.when('GET', '/api/fibonnacci/get').respond('0,1,1,2,3');

        datacontext.getDefaultFibonnacci().then(function (data) { sequence = data; });

        $rootScope.$digest();

        expect(sequence).toEqual('0,1,1,2,3');
    });

Upvotes: 1

Related Questions