如何在控制器中对一个函数进行单元测试(使用Jasmine),该控制器调用返回promise

时间:2016-04-06 17:18:23

标签: angularjs unit-testing jasmine angularjs-factory angular-mock

在下面的 SampleController 中,如何对 postAttributes 函数调用 sampleService.updateMethod 即可。我遇到了麻烦,因为updateMethod返回了promise。

    angular.module('sampleModule')
       .controller('SampleController', SampleController);

    SampleController.$inject =['sampleService'];

    function SampleController(sampleService){

    this.postAttributes = function() {    
        sampleService.updateMethod(number,attributes)
            .then(function(response){
                //do something on successful update
            },function(response){
                //do something on unsuccessful update
            });
        }; 

    }

这是我的工厂服务:

    angular.module('sampleModule')
        .factory('sampleService', sampleService);

    sampleService.$inject = ['$http'];

    function sampleService($http) {
        return {
            getMethod: function(acctNumber){
                return $http({
                    method: 'GET',
                    url: //api endpoint
                });
            },
            updateMethod: function(number, attributes){
                return $http({
                    method: 'PUT',
                    url: //api endpoint,
                    data: //payload
                });
            }
        };
    }

我想在控制器规范中模拟工厂服务,而不是直接将实际服务注入$ controller,因为大多数单元测试指南都指定在隔离下测试一个单元。

样本控制器规范:

    describe('SampleController Test', function(){
        var $controller;
        var service;

        beforeEach(angular.mock.module('sampleModule')); 

        beforeEach(angular.mock.inject(function(_$controller_){
            $controller = _$controller_;
        }));

        it('Testing $scope variable', function(){
            var sampleController = $controller('SampleController', { 
                sampleService: service, //mocked factory service 
            });

            sampleController.postAttributes(); //calling the function first
            //here I would like to make an assertion to check if
            //sampleService.updateMethod has been called with certain parameters                        
            //how do I do that??
        });

    });

2 个答案:

答案 0 :(得分:3)

用Google搜索并找到一个模拟工厂服务的解决方案,并遵循@TehBeardedOne等承诺创建方法,并让它从模拟服务中返回。

    describe('SampleController', function(){

        var mockService, controller, deferred, $rootScope;

        beforeEach(function(){

            angular.mock.module('sampleModule');

            angular.mock.module(function($provide){
                $provide.factory('sampleService', function($q){
                    function updateMethod(acct, attr){
                        deferred = $q.defer();
                        return deferred.promise;
                    }
                    return{updateMethod: updateMethod};
                });
            });

            angular.mock.inject(function($controller, sampleService, _$rootScope_){
                $rootScope = _$rootScope_;
                mockService = sampleService;
                spyOn(mockService, 'updateMethod').and.callThrough();
                controller =$controller('SampleController', {
                    sampleService: mockService,
                })
            });
        });

        it('postAttributes function should call updateMethod', function(){

            controller.postAttributes();
            expect(mockService.updateMethod).toHaveBeenCalled();
            expect(mockService.updateMethod).toHaveBeenCalledWith(controller.accountNumber, controller.attributes);
        });

        it('postAttributes success block', function(){
            controller.postAttributes();
            var res = {
                data: '2323'
            }
            deferred.resolve(res);
            $rootScope.$digest();
            expect(//something in success block).toBe(something);
        });

        it('postAttributes failure block', function(){
            controller.postAttributes();
            var res = {
                data: '9898'
            }
            deferred.reject(res);
            $rootScope.$digest();
            expect(controller.lame).toBe('dont type shit');
        });

    });

我使用 $ provider 服务嘲笑 sampleService ,并使 updateMethod < / strong>使用 $ q 返回承诺。稍后,您可以解决或拒绝承诺,并测试其阻止的成功和失败块。

答案 1 :(得分:1)

您需要使用间谍来检查是否正在调用该函数。有几种方法可以做到这一点,因为我确定你可能已经注意到了。您还需要注入$q$rootScope,因为您需要循环摘要周期以返回承诺。

事实上,如果你想要做的就是检查是否正在调用该函数,那么简单的间谍就可以了。你甚至不必退还诺言。话虽这么说,如果你想继续通过postAttributes()函数并测试这个函数中的其他东西,那么你需要返回承诺来做到这一点。这是适合我的方法。这样的事情应该有效。

describe('SampleController Test', function(){
    var $controller;
    var service;
    var $q;
    var $rootScope;
    var updateMethodDeferred;
    var ctrl;
    var mockHttpObject;

    beforeEach(angular.mock.module('sampleModule'));

    beforeEach(angular.mock.inject(function(_sampleService_, _$q_, _$rootScope_, _$controller_){
        service = _sampleService_;
        $q = _$q_;
        $rootScope = _$rootScope_;
        $controller = _$controller_;

        mockHttpObject = {
            //add mock properties here
        }

        updateMethodDeferred = $q.defer();
        spyOn(service, 'updateMethod').and.returnValue(updateMethodDeferred.promise);

        ctrl = $controller('SampleController', {
            sampleService: service, //mocked factory service
        });
    }));

    it('Testing $scope variable', function(){
        ctrl.postAttributes();
        expect(service.updateMethod).toHaveBeenCalled();
    });

    it('Should do something after promise is returned', function(){
        ctrl.postAttributes();
        updateMethodDeferred.resolve(mockHttpObject);
        $rootScope.$digest();
        expect(/*something that happened on successful update*/).toBe(/*blah*/)           
    });
});