Angular - 模拟 returns 承诺的服务方法

Angular - mocking out service method that returns a promise

我正在尝试测试依赖于服务调用来获取一些数据的控制器方法。服务方法 returns 一个承诺,如果承诺被解决或拒绝,我想测试控制器的行为。

我想出了这种方法来改变我的模拟服务方法的行为,但它不起作用。当调用模拟的 getData 方法时,getDataSuccess 标志始终为真。这是我目前所拥有的:

控制器:

app.controller('myController', function($scope, myService) {
       myService.getData()
         .then(function (data) {
             $scope.data = data;                   
         },
         function (data) {
             $scope.serverError = data;
         });
});

测试:

describe('myController', function () {

    var ctl, serviceMock, getDataSuccess, scope;

    beforeEach(function() {
            getDataSuccess = true;
            serviceMock = {};

        module('app', function ($provide) {
            $provide.value('myService', serviceMock);
        });      

        inject(function ($q) {
            serviceMock.getData = function () {                
                var defer = $q.defer();
                if (getDataSuccess) {
                    defer.resolve("theData");
                } else {
                    defer.reject("theData");
                }
                return defer.promise;
            };
        });
    });

    beforeEach(inject(function ($rootScope, $controller, $httpBackend, myService) {

        scope = $rootScope.$new();

        ctl = $controller('myController', {
            $scope: scope,          
            myService: myService,
        });        
    }));

    describe('myController loading data', function () {

        it('should set $scope.data if data load succeeds', function () {
            getDataSuccess = true;
            scope.$apply();
            expect(scope.data).toEqual("theData");
        });

        it('should set $scope.serverError if data load fails', function () {
            getDataSuccess = false;
            scope.$apply();
            expect(scope.serverError).toEqual("theData");
        });

    });

});

显然我在这里遗漏了一些东西。执行顺序不是我所期望的。做这种事情的正确方法是什么?

这是 Plunker 中的示例:http://plnkr.co/edit/ODyslivLorjaLM4EqlEF?p=preview

myService.getData 函数在初始化 myController 的地方被调用。所以如果你想通过设置getDataSuccess来改变getData函数的行为,你需要在设置getDataSuccess后初始化myController true/false.

我推荐的是这样的

在appSpec.js

describe('myController', function () {

    var ctl, serviceMock, getDataSuccess, scope;

    beforeEach(function() {
        getDataSuccess = true;
        serviceMock = {};

        module('app', function ($provide) {
            $provide.value('myService', serviceMock);
        });      

        inject(function ($q) {
            serviceMock.getData = function () {                
                var defer = $q.defer();
                if (getDataSuccess) {
                    defer.resolve("theData");
                } else {
                    defer.reject("theData");
                }
                return defer.promise;
            };
        });
    });

    beforeEach(inject(function ($rootScope, $controller, $httpBackend, myService) {
        scope = $rootScope.$new();
//      
//        ctl = $controller('myController', {
//            $scope: scope,          
//            myService: myService,
//        });        
    }));

    describe('myController loading data', function () {

        it('should set $scope.data if data load succeeds', inject(function($controller, myService){
            getDataSuccess = true;
            ctl = $controller('myController', {
                $scope: scope,          
                myService: myService,
            });        
            scope.$apply();
            expect(scope.data).toEqual("theData");
        }));    

        it('should set $scope.serverError if data load fails', inject(function($controller, myService){
            getDataSuccess = false;
            ctl = $controller('myController', {
                $scope: scope,          
                myService: myService,
            });        
            scope.$apply();
            expect(scope.serverError).toEqual("theData");
        }));    

    });

});

This is updated plunker.