【问题标题】:Angular controller promises and testingAngular 控制器承诺和测试
【发布时间】:2014-06-04 03:50:33
【问题描述】:

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

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。它在控制器中。像这样:

编辑: $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;
  }

【问题讨论】:

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

标签: javascript angularjs unit-testing promise q


【解决方案1】:

您的 UserService 中有几个问题。

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

  • 你实际上是在getUser 中创建一个承诺,而实际上你并不需要(可以看作anti-pattern)。从$httppromise 返回的promise 的success 函数我认为往往比它的价值更麻烦。根据我的经验,通常最好只使用标准的 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;     
});

然后在测试中,解决promise call $apply之后。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-15
    • 1970-01-01
    • 2016-05-01
    • 2016-07-25
    • 2017-06-03
    • 2018-12-28
    • 1970-01-01
    • 2017-02-27
    相关资源
    最近更新 更多