CaptainMorgan
CaptainMorgan

Reputation: 1253

Karma testing controller that calls service with http

Can someone please tell me the best way to run tests on my controller function getData and the factory function too. I've very confused and don't know where to start. How would you write tests for the code below?

myApp.controller('myController', ['$scope', 'myFactory', function ($scope, myFactory) {

    $scope.getData = function(id) {
        var promise = myFactory.GetData('/dta/GetData?Id=' + id);
        promise
        .then(function (success) {
            $scope.result = success;
        }, function (error) {
            $scope.error = true;
        });
    }
});


myApp.factory('myFactory', ['$http', function ($http) {
    return {
        GetData: function (url) {
            return $http.get(url)
            .then(function (response) {
                return response.data;
             }, function (error) {
                return error;
             });
        }
    }
}]);

Upvotes: 0

Views: 668

Answers (1)

Phil
Phil

Reputation: 164970

You'll want to test each component in isolation (that's what unit tests are for). So something like this for the controller

describe('myController test', () => {
    let scope, myFactory;

    beforeEach(() => {
        myFactory = jasmine.createSpyObj('myFactory', ['GetData']);            

        module('your-module-name');
        inject(function($rootScope, $controller) {
            scope = $rootScope.$new();

            $controller('myController', {
                $scope: scope,
                myFactory: myfactory
            });
        });
    });

    it('getData assigns result on success', inject(function($q) {
        let id = 1, success = 'success';
        myFactory.GetData.and.returnValue($q.when(success));

        scope.getData(id);
        expect(myFactory.GetData).toHaveBeenCalledWith('/dta/GetData?Id=' + id);
        scope.$digest(); // resolve promises
        expect(scope.result).toBe(success);
    }));

    it('getData assigns error on rejections', inject(function($q) {
        myFactory.GetData.and.returnValue($q.reject('error'));

        scope.getData('whatever');
        scope.$digest();
        expect(scope.error).toEqual(true);
    }));
});

For your factory, you would create a separate describe and inject and configure $httpBackend. There are plenty of example in the documentation.


FYI, you should omit the error handler in your factory, ie

return $http.get(url).then(response => response.data);

or if you don't like ES2015

return $http.get(url).then(function(response) {
    return response.data;
});

as you are currently converting a failed request into a successful promise.


In fact, I'd go a bit further to make your GetData factory more useful than a mere $http wrapper

GetData: function(id) {
    return $http.get('/dta/GetData', {
        params: { Id: id }
    }).then(function(res) {
        return res.data;
    });
}

Upvotes: 1

Related Questions