Angularjs承诺不会在单元测试中解决

rob*_*phy 21 javascript unit-testing jasmine angularjs

我正在使用jasmine对一个angularjs控制器进行单元测试,该控制器将范围上的变量设置为调用返回promise对象的服务方法的结果:

var MyController = function($scope, service) {
    $scope.myVar = service.getStuff();
}
Run Code Online (Sandbox Code Playgroud)

服务内部:

function getStuff() {
    return $http.get( 'api/stuff' ).then( function ( httpResult ) {
        return httpResult.data;
    } );
}
Run Code Online (Sandbox Code Playgroud)

这在我的angularjs应用程序的上下文中工作正常,但在茉莉花单元测试中不起作用.我已确认"then"回调正在单元测试中执行,但$ scope.myVar承诺永远不会被设置为回调的返回值.

我的单元测试:

describe( 'My Controller', function () {
  var scope;
  var serviceMock;
  var controller;
  var httpBackend;

  beforeEach( inject( function ( $rootScope, $controller, $httpBackend, $http ) {
    scope = $rootScope.$new();
    httpBackend = $httpBackend;
    serviceMock = {
      stuffArray: [{
        FirstName: "Robby"
      }],

      getStuff: function () {
        return $http.get( 'api/stuff' ).then( function ( httpResult ) {
          return httpResult.data;
        } );
      }
    };
    $httpBackend.whenGET( 'api/stuff' ).respond( serviceMock.stuffArray );
    controller = $controller( MyController, {
      $scope: scope,
      service: serviceMock
    } );
  } ) );

  it( 'should set myVar to the resolved promise value',
    function () {
      httpBackend.flush();
      scope.$root.$digest();
      expect( scope.myVar[0].FirstName ).toEqual( "Robby" );
    } );
} );
Run Code Online (Sandbox Code Playgroud)

此外,如果我将控制器更改为以下单元测试通过:

var MyController = function($scope, service) {
    service.getStuff().then(function(result) {
        $scope.myVar = result;
    });
}
Run Code Online (Sandbox Code Playgroud)

为什么promise单元测试中的promise回调结果值没有传播到$ scope.myVar?请参阅以下jsfiddle以获取完整的工作代码http://jsfiddle.net/s7PGg/5/

pko*_*rce 21

我想这个"神秘"的关键在于AngularJS会自动解析promises(和渲染结果),如果在模板中的插值指令中使用的那些.我的意思是给定这个控制器:

MyCtrl = function($scope, $http) {
  $scope.promise = $http.get('myurl', {..});
}
Run Code Online (Sandbox Code Playgroud)

和模板:

<span>{{promise}}</span>
Run Code Online (Sandbox Code Playgroud)

AngularJS,在$ http调用完成后,将"看到"承诺已解决,并将使用已解析的结果重新呈现模板.这是$ q文档中含糊其词的内容:

$ q promises被临界引擎识别为angular,这意味着在模板中,您可以将附加到范围的promise视为结果值.

可以在这里看到这种魔法发生的代码.

但是,只有在游戏中存在模板($parse服务,更准确)时,才会发生这种"神奇" .在单元测试中,没有涉及模板,因此未自动传播承诺解析.

现在,我必须说这种自动分辨率/结果传播非常方便,但可能会让人感到困惑,正如我们从这个问题中看到的那样.这就是为什么我更喜欢像你一样明确地传播分辨率结果:

var MyController = function($scope, service) {
    service.getStuff().then(function(result) {
        $scope.myVar = result;
    });
}
Run Code Online (Sandbox Code Playgroud)

  • 在Angular 1.2中,承诺不再自动解决(AKA,unwrapped). (3认同)

Kev*_*key 8

我有一个类似的问题,让我的控制器直接将$ scope.myVar分配给promise.然后在测试中,我链接了另一个承诺,当它得到解决时断言承诺的期望值.我使用了这样的辅助方法:

var expectPromisedValue = function(promise, expectedValue) {
  promise.then(function(resolvedValue) {
    expect(resolvedValue).toEqual(expectedValue);
  });
}
Run Code Online (Sandbox Code Playgroud)

请注意,根据您调用expectPromisedValue的时间顺序以及当您的代码在测试中解析时,您可能需要手动触发最终的摘要周期才能运行以获取这些then()方法 - 没有它您的无论是否resolvedValue等于,测试都可以通过expectedValue.

为了安全起见,将触发器置于afterEach()调用中,这样您就不必为每次测试都记住它:

afterEach(inject(function($rootScope) {
  $rootScope.$apply();
}));
Run Code Online (Sandbox Code Playgroud)