問題描述
我正在使用 jasmine 對 angularjs 控制器進行單元測試,該控制器將作用域上的變量設置為調用返回 promise 對象的服務方法的結果:
I am using jasmine to unit test an angularjs controller that sets a variable on the scope to the result of calling a service method that returns a promise object:
var MyController = function($scope, service) {
$scope.myVar = service.getStuff();
}
服務內部:
function getStuff() {
return $http.get( 'api/stuff' ).then( function ( httpResult ) {
return httpResult.data;
} );
}
這在我的 angularjs 應用程序的上下文中工作正常,但在 jasmine 單元測試中不起作用.我已經確認then"回調正在單元測試中執行,但 $scope.myVar 承諾永遠不會設置為回調的返回值.
This works fine in the context of my angularjs application, but does not work in the jasmine unit test. I have confirmed that the "then" callback is executing in the unit test, but the $scope.myVar promise never gets set to the return value of the callback.
我的單元測試:
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" );
} );
} );
另外,如果我將控制器更改為以下單元測試通過:
Also, if I change the controller to the following the unit test passes:
var MyController = function($scope, service) {
service.getStuff().then(function(result) {
$scope.myVar = result;
});
}
為什么在單元測試中promise回調結果值沒有被傳播到$scope.myVar?有關完整的工作代碼,請參閱以下 jsfiddle http://jsfiddle.net/s7PGg/5/
Why is the promise callback result value not being propagated to $scope.myVar in the unit test? See the following jsfiddle for full working code http://jsfiddle.net/s7PGg/5/
推薦答案
我猜這個謎團"的關鍵在于,如果在模板.我的意思是,給定這個控制器:
I guess that the key to this "mystery" is the fact that AngularJS will automatically resolve promises (and render results) if those used in an interpolation directive in a template. What I mean is that given this controller:
MyCtrl = function($scope, $http) {
$scope.promise = $http.get('myurl', {..});
}
和模板:
<span>{{promise}}</span>
AngularJS 在 $http 調用完成后,將看到"一個承諾已解決,并將使用已解決的結果重新渲染模板.這就是 $q 文檔中隱約提到的內容:
AngularJS, upon $http call completion, will "see" that a promise was resolved and will re-render template with the resolved results. This is what is vaguely mentioned in the $q documentation:
$q Promise 被 Angular 模板引擎識別,它意味著在模板中,您可以將附加到范圍的承諾視為如果它們是結果值.
$q promises are recognized by the templating engine in angular, which means that in templates you can treat promises attached to a scope as if they were the resulting values.
可以在這里查看代碼.
但是,這種魔法"只有在有模板(更準確地說是 $parse
服務)在起作用時才會發生.在您的單元測試中不涉及模板,因此不會自動傳播承諾解析.
BUT, this "magic" happens only when there is a template ($parse
service, to be more precise) at play. In your unit test there is no template involved so promise resolution is not propagated automatically.
現在,我必須說這種自動解析/結果傳播非常方便,但可能會令人困惑,正如我們從這個問題中看到的那樣.這就是為什么我更喜歡像您一樣顯式傳播解析結果:
Now, I must say that this automatic resolution / result propagation is very convenient but might be confusing, as we can see from this question. This is why I prefer to explicitly propagate resolution results as you did:
var MyController = function($scope, service) {
service.getStuff().then(function(result) {
$scope.myVar = result;
});
}
這篇關于Angularjs 承諾不會在單元測試中解決的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!