角度控制器承诺和测试

时间:2014-04-19 19:19:32

标签: javascript angularjs unit-testing promise q

我正在为我的控制器编写一些使用promises的单元测试。 基本上这个:

UserService.getUser($routeParams.contactId).then(function (data) {
      $scope.$apply(function () {
      $scope.contacts = data;
   });
});

我已经模拟了我的UserService。这是我的单元测试:

beforeEach(inject(function ($rootScope, $controller, $q, $routeParams) {
        $routeParams.contactId = contactId;
        window.localStorage.clear();
        UserService = {
            getUser: function () {
                def = $q.defer();
                return def.promise;
            }
         };
        spyOn(UserService, 'getUser').andCallThrough();


      scope = $rootScope.$new();
      ctrl = $controller('ContactDetailController', {
        $scope: scope,
        UserService:UserService
      });
  }));


it('should return 1 contact', function () {
    expect(scope.contacts).not.toBeDefined();
    def.resolve(contact);
    scope.$apply();

    expect(scope.contacts.surname).toEqual('NAME');
    expect(scope.contacts.email).toEqual('EMAIL');
});

这给我以下错误:

Error: [$rootScope:inprog] $digest already in progress

现在删除控制器中的$ scope。$ apply会导致测试通过,如下所示:

UserService.getUser($routeParams.contactId).then(function (data) {
    $scope.contacts = data;     
 });

然而,这打破了我的控制器的功能......那么我该怎么办?

感谢您的回复,$ apply不会发生在UserService中。它在控制器中。像这样:

编辑: $ apply正在这样的控制器中发生。

appController.controller('ContactDetailController', function ($scope, $routeParams, UserService) {
    UserService.getUser($routeParams.contactId).then(function (data) {
      $scope.$apply(function () {
        $scope.contacts = data;
    });
});

Real UserService:

 function getUser(user) {
    if (user === undefined) {
      user = getUserId();
    }
    var deferred = Q.defer();
    $http({
      method: 'GET',
      url: BASE_URL + '/users/' + user
    }).success(function (user) {
      deferred.resolve(user);
    });
    return deferred.promise;
  }

1 个答案:

答案 0 :(得分:1)

您的UserService中存在一些问题。

  • 您使用的是Q,而不是$q。很难确切地知道它具有什么效果,除了它在使用Angular时不常见,并且可能会影响then回调运行的确切时间。

  • 当你真的不需要时,你实际上是在getUser创建一个承诺(可以看作是anti-pattern)。从success承诺返回的承诺的$http函数我认为通常比它的价值更麻烦。根据我的经验,通常最好只使用标准then函数,因为您可以为它返回一个经过后处理的值并使用标准的promise链接:

    function getUser(user) {
      if (user === undefined) {
        user = getUserId();
      }
      return $http({
        method: 'GET',
        url: BASE_URL + '/users/' + user
      }).then(function(response) {
        return response.data;
      });
    }
    

更改上述内容后,控制器代码可以更改为

UserService.getUser($routeParams.contactId).then(function (data) {
  $scope.contacts = data;     
});

然后在测试中,在解析承诺后调用$apply

def.resolve(contact);
scope.$apply();