使用AngularJS和Jasmine模拟HTTP服务单元测试

时间:2014-11-27 16:02:14

标签: angularjs unit-testing mocking jasmine

我正在尝试构建一个模拟服务,以便我的单元测试可以验证某些函数是否被调用并相应地更新。不幸的是,我无法让它发挥作用。

我目前在此行收到错误undefined is not a function

spyOn(statusService, 'getModuleStatus').andCallThrough();

我的实际服务如下:

serviceStatusServices.factory('serviceStatusAppAPIservice', function ($http) {

    var serviceStatusAppAPI = {};

    serviceStatusAppAPI.getModuleStatus = function () {
        return $http({
            method: 'JSON',
            url: '/settings/getservicestatusandconfiguration'
        });
    }

    serviceStatusAppAPI.setModuleStatus = function (module) {
        return $http({
            method: 'POST',
            url: '/settings/setservicestatusandconfiguration',
            data: { moduleId: module.ModuleId, configData: module.ConfigValues }
        });
    }

    return serviceStatusAppAPI;
});

我的更新功能

serviceStatusControllers.controller('serviceStatusController', ['$scope', 'serviceStatusAppAPIservice', '$filter', '$timeout', function ($scope, serviceStatusAppAPIservice, $filter, $timeout) {

    $scope.update = function () {
        $scope.loading = true;
        serviceStatusAppAPIservice.getModuleStatus().then(function (response) {

            $scope.modules = $filter('orderBy')(response.data.moduleData, 'ModuleName')
            ...

我的测试看起来像这样

describe('ServiceStatusController', function () {
    beforeEach(module("serviceStatusApp"));

    var scope;
    var statusService;
    var controller;
    var q;
    var deferred;

    // define the mock people service
    beforeEach(function () {
        statusService = {
            getModuleStatus: function () {
                deferred = q.defer();
                return deferred.promise;
            }
        };
    });

    // inject the required services and instantiate the controller
    beforeEach(inject(function ($rootScope, $controller, $q) {
        scope = $rootScope.$new();
        q = $q;
        controller = $controller('serviceStatusController', { 
                     $scope: scope, serviceStatusAppAPIservice: statusService });
    }));

    describe("$scope.update", function () {
        it("Updates screen", function () {
            spyOn(statusService, 'getModuleStatus').andCallThrough();

            scope.update();

            deferred.resolve();

            expect(statusService.getModuleStatus).toHaveBeenCalled();
            expect(scope.modules).not.toBe([]);
        });
    });
});

另外,如何将从服务返回的任何模拟数据传递给调用者。目前在我的模型中,我serviceStatusAppAPI.getModuleStatus(data)然后使用data.Data来获取返回的JSON。

1 个答案:

答案 0 :(得分:0)

我假设你在ctrl中做了类似的事情

scope.update = function() {
    serviceStatusAppAPIservice.setModuleStatus(url).then(function (data) {
        scope.modules = data;
    })    
};

返回承诺的服务

.factory('serviceStatusAppAPI', function($http, $q) {
        return {
            getModuleStatus: function() {
                var defer = $q.defer();
                $http({method: 'GET', url: '/settings/getservicestatusandconfiguration'})
                    .success(function(data, status, headers, config) {
                        // this callback will be called asynchronously
                        // when the response is available
                        defer.resolve(data);
                    })
                    .error(function(data, status, headers, config) {
                        // called asynchronously if an error occurs
                        // or server returns response with an error status.
                        window.data = data;
                    });

                return defer.promise;
            }
        };
    });

所以在你的控制器中你会得到这样的数据

serviceStatusAppAPI.getModuleStatus().then(function (data) {
    $scope.modules = $filter('orderBy')(data.moduleData, 'ModuleName')
})

这是您运行单元测试用例的方法

beforeEach(function() {

    var statusService = {};

    module('myApp', function($provide) {
        $provide.value('serviceStatusAppAPIservice', statusService);
    });

    statusService.modalStatus = {
        moduleData: [{ModuleName: 'abc'}, {ModuleName: 'def'}]
    };

    inject(function ($q) {
        statusService.setModuleStatus = function () {
            var defer = $q.defer();

            defer.resolve(this.modalStatus);

            return defer.promise;
        };

        statusService.getModuleStatus = function () {
            var defer = $q.defer();

            defer.resolve(this.modalStatus);

            return defer.promise;
        };

    });
});

beforeEach(inject(function ($rootScope, $controller, _$stateParams_) {
    scope = $rootScope.$new();
    stateParams = _$stateParams_;
    controller = $controller;
}));

var myCtrl = function() {
    return controller('ServiceStatusController', {
                $scope: scope,
            });
};

it('should load status', function () {
    myCtrl();
    scope.update();
    scope.$digest();
    expect(scope.modules).toBe({
        status: 'active'
    });
});