我正在使用茉莉花对angularjs控制器进行单元测试,该控制器在范围内将变量设置为调用返回promise对象的服务方法的结果:
var MyController = function($scope, service) { $scope.myVar = service.getStuff(); }
服务内部:
function getStuff() { return $http.get( 'api/stuff' ).then( function ( httpResult ) { return httpResult.data; } ); }
在我的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" ); } ); } );
另外,如果我将控制器更改为以下单元测试通过:
var MyController = function($scope, service) { service.getStuff().then(function(result) { $scope.myVar = result; }); }
为什么在单元测试中未将promise回调结果值传播到$ scope.myVar?有关完整的工作代码,请参见以下jsfiddle:http://jsfiddle.net/s7PGg/5/
我想这个“谜”的关键在于,如果在模板的插值指令中使用了承诺,AngularJS将自动解决承诺(并渲染结果)。我的意思是给定此控制器:
MyCtrl = function($scope, $http) { $scope.promise = $http.get('myurl', {..}); }
和模板:
<span>{{promise}}</span>
在$ http调用完成后,AngularJS将“看到”一个承诺已解决,并将使用已解决的结果重新呈现模板。这就是$ q文档中模糊提及的内容:
$ qpromise由模板引擎以角度识别,这意味着在模板中,您可以将附加到作用域的promise视为它们的结果值。
可以在此处看到发生这种魔术的代码。
但是,只有在有模板($parse更准确地说是服务)在播放时,这种“魔术”才会发生。 在您的单元测试中,不涉及模板,因此promise解析不会自动传播 。
$parse
现在,我必须说这种自动解析/结果传播非常方便,但可能会造成混淆,正如我们从这个问题可以看到的那样。这就是为什么我更喜欢像您那样显式传播分辨率结果的原因: