【问题标题】:Mocking HTTP service unit test with AngularJS and Jasmine使用 AngularJS 和 Jasmine 模拟 HTTP 服务单元测试
【发布时间】:2014-11-27 16:02:14
【问题描述】:

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

我目前在此行收到错误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。

【问题讨论】:

    标签: angularjs unit-testing mocking jasmine


    【解决方案1】:

    我假设如果你在你的 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'
        });
    });
    

    【讨论】:

    • 嗨,谢谢,是的,你基本上是正确的。我已经用我的更新功能更新了我的问题。我目前在getModuleStatus 上遇到错误
    • 尝试在模拟服务中提供您期望的准确数据。我认为既然您正在应用 $filter 并期望从中得到一些东西。您需要提供准确的数据,以便 $filter 可以过滤结果集
    • 可能是这样的 {moduleData: [{moduleName: 'Module1'}, {moduleName: 'newmodule'}]}
    • 我认为它更多地抱怨then,因为它说未定义不是一个函数,好像serviceStatusAppAPIservice.getModuleStatus() 是未定义的
    • 我刚刚更新了我的问题以显示我的控制器签名(如果有帮助的话)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-22
    • 1970-01-01
    • 2014-07-05
    相关资源
    最近更新 更多