【问题标题】:How can I avoid "$digest already in progress" error in my AngularJS test?如何在 AngularJS 测试中避免“$digest already in progress”错误?
【发布时间】:2015-11-18 09:49:41
【问题描述】:

我在使用 Chrome 在 Karma 中运行我的 AngularJS 测试之一时遇到问题。该测试在 PhantomJS 中运行。

使用 Chrome 运行测试时出现以下错误:

Chrome 46.0.2490 (Windows 7 0.0.0) ERROR
  Uncaught Error: [$rootScope:inprog] $digest already in progress
  http://errors.angularjs.org/1.3.18/$rootScope/inprog?p0=%24digest
  at C:/dev/unionvms_eu/branches/dev/unionvms-web/bower_components/angular-mocks/angular-mocks.js:275

测试:

describe('todoRestService', function() {

    beforeEach(module('todoApp'));

    //MOCK RESOURCE
    beforeEach(function () {
        var mockResource = {
            todo: function() {
                return {
                    update: function(todoDTO, callback) {
                        callback({
                            code : 200,
                            data: {
                                status : 'CLOSED'
                            }
                        });
                    },
                }
            },
        };

        module(function ($provide) {
            $provide.value('todoRestFactory', mockResource);
        });

    });

    it("updateTodoStatus should send request to backend and return received object", inject(function($rootScope, $log, todoRestService, Todo) {
        var todo = new Todo();
        todo.status = "CLOSED";

        var resolved = false;
        $rootScope.$apply(function(){
            todoRestService.updateTodoStatus(todo).then(function(updatedTodo){
                resolved = true;
                expect(updatedTodo.status).toEqual(todo.status);
            });
        });
        $rootScope.$digest();
        expect(resolved).toBe(true);
    }));

});

代码:

angular.module('todoApp')
    .factory('todoRestFactory',function($resource) {
        return {
            todo : function(){
                return $resource('/app/todo', {}, {
                    update: {method: 'PUT'}
                });
            },
        };
    })
.factory('todoRestService', function($q, $log, todoRestFactory, Todo){

    var updateTodoStatus = function(todo){
        var deferred = $q.defer();
        todoRestFactory.todo().update(todo.DTO(), function(response) {
            if(response.code !== 200){
                deferred.reject("Invalid response status");
                return;
            }
            deferred.resolve(Todo.fromDTO(response.data));
        }, function(error) {
            $log.error("Error updating todo status");
            $log.error(error);
            deferred.reject(error);
        });
        return deferred.promise;
    };

    return {
        updateTodoStatus: updateTodoStatus,
    };
});

如果我从测试中删除$rootScope.$digest();,错误就会消失,但最后一个断言将失败。我怎样才能使测试工作?

【问题讨论】:

    标签: javascript angularjs unit-testing testing


    【解决方案1】:

    您需要检查 $digest 是否已经启动。 你可以这样做

     if(!$scope.$$phase) {
     //do smth here
    }
    

    【讨论】:

    • 我尝试在单元测试中包装$rootScope.$digest();,但我仍然得到同样的错误。
    • $rootScope.apply 必须自己触发 $digest。但是你可以尝试使用 $applyAsync() 而不是 $apply。
    • 不确定你的意思。
    • 如果我没记错的话,您正在处理类似异步方法。对它们强制使用 $digest 的最佳方法是在作用域上调用 $asyncApply。试试这个。
    【解决方案2】:

    我认为有 3 种方法可以解决这个问题:

    • 编写一个如下所示的 safeApply 函数:

      $scope.safeApply = function(fn) {
        var 阶段 = this.$root.$$phase;
        如果(阶段 === '$apply' || 阶段 === '$digest'){
          if(fn && (typeof(fn) === 'function')) {
            fn();
          }
        } 别的 {
          这个.$apply(fn);
        }
      };
    • 你也可以使用safeApply组件

    在代码中:

    $scope.$safeApply();
    
    • $timeout 中调用$apply

      $timeout($scope.$apply, 0);

    【讨论】:

    • 谢谢,但我不太明白你的意思。我应该把$scope.$safeApply();放在哪里?我应该在哪里调用 $timeout?
    • 在你的代码中:` var todo = new Todo(); todo.status = "关闭"; var已解决=假; $rootScope.$safeApply(function(){ todoRestService.updateTodoStatus(todo).then(function(updatedTodo){ resolved = true; expect(updatedTodo.status).toEqual(todo.status); }); });期望(已解决).toBe(真);`
    • 以更常见的方式,只需将所有 $apply 替换为 $safeApply !
    • 你是否用 $safeApply 替换了所有的 $apply?你应该删除你的 $rootScope.$digest();也! $apply 进行摘要。
    • 我将 apply 替换为 safeApply 并在单元测试中删除了 digest() 但它不起作用。在服务中,根本没有apply。
    猜你喜欢
    • 2017-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多