AngularJSのpromiseが解決した結果が伝搬するには、$digestループを開始する必要がある

こんにちは。牡蠣のパスタをたべました。きたけーです。

今日、Angularでこんなテストコードを書いたのですが、promiseを解決したときに実行されるコールバックが実行されなくて困りました。

describe 'Controller: UserController', ->
  beforeEach module 'SomeApp'

  UserController = {}
  scope = {}

  beforeEach inject ($controller, $rootScope, User, $q) ->
    scope = $rootScope.$new()

    spyOn(User, 'query').and.callFake ->
      deferred = $q.defer()
      deferred.resolve {name: "kitak"}
      deferred.promise

    UserController = $controller 'UserController', {
      $scope: scope
    }

  it 'should attach user name to the scope', ->
    # user.name が undefined でテストが落ちてしまう。
    expect(scope.user.name).toBe "kitak"

AngularJS - why is $apply required to properly resolve a $q promise? - Stack Overflow によると、promiseを解決した場合に、結果はすぐに伝搬せずに、$digestループの中で伝搬するとのこと。 このテストコードでは(当然ですが)ブラウザのイベントが発生したりはしないので、$digestループは開始しません。

なので、scope.$apply()で明示的に$digestループを開始させてあげることでテストが通ります。

  it 'should attach user name to the scope', ->
    scope.$apply()
    expect(scope.user.name).toBe "kitak"