Javascript 角度控制器的设计与测试

Javascript 角度控制器的设计与测试,javascript,angularjs,unit-testing,promise,q,Javascript,Angularjs,Unit Testing,Promise,Q,我正在为我的控制器编写一些单元测试,它使用承诺。 基本上: UserService.getUser($routeParams.contactId).then(function (data) { $scope.$apply(function () { $scope.contacts = data; }); }); 我嘲笑了我的用户服务。这是我的单元测试: beforeEach(inject(function ($rootScope, $controller, $q,

我正在为我的控制器编写一些单元测试,它使用承诺。 基本上:

UserService.getUser($routeParams.contactId).then(function (data) {
      $scope.$apply(function () {
      $scope.contacts = data;
   });
});
我嘲笑了我的用户服务。这是我的单元测试:

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;     
 });
然而,这破坏了我的控制器的功能。。。那我在这里该怎么办

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

编辑: $apply在控制器中发生的情况如下

appController.controller('ContactDetailController', function ($scope, $routeParams, UserService) {
    UserService.getUser($routeParams.contactId).then(function (data) {
      $scope.$apply(function () {
        $scope.contacts = data;
    });
});
实时用户服务:

 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;
  }

您的用户服务中有几个问题

  • 您使用的是
    Q
    ,而不是
    $Q
    。很难确切知道这有什么影响,除了使用Angular时它不是典型的,并且可能会影响
    然后
    回调运行的确切时间

  • 实际上,您正在
    getUser
    中创建一个承诺,而实际上您并不需要这样做(可以看作是一个承诺)。我认为,
    $http
    promise返回的promise的
    成功
    功能通常比它的价值更麻烦。根据我的经验,通常最好只使用标准的
    then
    函数,因为这样您就可以为它返回一个经过后期处理的值,并使用标准的承诺链接:

    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();

在测试承诺时,您需要手动运行摘要。是的,您应该在单元测试中手动运行摘要,而您可能不应该在服务中这样做。为什么您的服务有$scope?服务应该充当模型,控制器应该从服务获取数据。如果您发现需要在代码中运行$apply,则可能有问题。作用域上的$apply发生在控制器中。很抱歉造成混淆,请参阅上面帖子中的我的编辑。
UserService
中可能存在问题,因为它正在解决
$digest
循环之外的承诺。你能发布它如何获取用户并解析其返回承诺的代码/详细信息吗?@MichalCharemza将其添加到上面的帖子中