Angularjs 약속이 단위 테스트에서 해결되지 않음
저는 jasmine을 사용하여 약속 객체를 반환하는 서비스 메서드를 호출한 결과에 범위의 변수를 설정하는 angularjs 컨트롤러를 단위 테스트하고 있습니다.
var MyController = function($scope, service) {
$scope.myVar = service.getStuff();
}
서비스 내부:
function getStuff() {
return $http.get( 'api/stuff' ).then( function ( httpResult ) {
return httpResult.data;
} );
}
이것은 제 angularjs 어플리케이션의 상황에서는 잘 작동하지만 재스민 단위 테스트에서는 작동하지 않습니다.유닛 테스트에서 "그때" 콜백이 실행되고 있음을 확인했지만 $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;
});
}
단위 테스트에서 약속 콜백 결과 값이 $scope.myVar로 전파되지 않는 이유는 무엇입니까?전체 작동 코드는 다음 jsfiddle 참조 http://jsfiddle.net/s7PGg/5/
이 '미스터리'의 핵심은 앵글이JS는 템플릿에서 보간 지시어에 사용된 약속을 자동으로 해결하고 결과를 렌더링합니다.제 말은 이 컨트롤러를 고려하면 다음과 같습니다.
MyCtrl = function($scope, $http) {
$scope.promise = $http.get('myurl', {..});
}
템플릿:
<span>{{promise}}</span>
$http 호출이 완료되면 AngularJS는 약속이 해결된 것을 "확인"하고 해결된 결과로 템플릿을 다시 렌더링합니다.$q 문서에서 모호하게 언급된 내용은 다음과 같습니다.
$q 약속은 템플릿 엔진에서 각도로 인식됩니다. 즉, 템플릿에서는 범위에 연결된 약속을 결과 값인 것처럼 처리할 수 있습니다.
이 마법이 일어나는 코드는 여기서 볼 수 있습니다.
" " " 이 " " 만 " 합니다 " " " " 합니다 " " " 만 " " " "$parse
서비스, 더 정확히 말하자면) 경기 중입니다.단위 테스트에는 템플릿이 포함되어 있지 않으므로 약속 해결이 자동으로 전파되지 않습니다.
이 질문에서 알 수 있듯이 자동 해결/결과 전파는 매우 편리하지만 혼란스러울 수 있습니다.이것이 제가 여러분이 했던 것처럼 해결 결과를 명시적으로 전파하는 것을 선호하는 이유입니다.
var MyController = function($scope, service) {
service.getStuff().then(function(result) {
$scope.myVar = result;
});
}
저도 비슷한 문제가 있어서 컨트롤러가 $scope.myVar를 약속에 직접 할당하도록 했습니다.그리고 시험에서는 그 약속이 해결되면 기대되는 가치를 주장하는 또 다른 약속을 사슬로 묶었습니다.나는 다음과 같은 도우미 방법을 사용했습니다.
var expectPromisedValue = function(promise, expectedValue) {
promise.then(function(resolvedValue) {
expect(resolvedValue).toEqual(expectedValue);
});
}
expectPromiseValue를 호출할 때의 순서와 테스트 중인 코드에 의해 약속이 해결될 때의 순서에 따라 최종 다이제스트 사이클을 수동으로 트리거하여 실행해야 할 수도 있습니다. 이 방법이 없으면 테스트가 통과될 수 있습니다.resolvedValue
과 동등한expectedValue
그렇지 않으면.
안전을 위해 트리거를 후() 통화에 넣어 모든 테스트에서 트리거를 기억할 필요가 없습니다.
afterEach(inject(function($rootScope) {
$rootScope.$apply();
}));
@pkozlowski.opensource는 그 이유(THANK YOU!)에 대해 답했지만 테스트에서 이 문제를 해결하는 방법은 답하지 않았습니다.
방금 도착한 해결책은 HTTP가 서비스에서 호출되고 있는지 테스트한 후 컨트롤러 테스트에서 서비스 방식을 염탐하여 약속 대신 실제 값을 반환하는 것입니다.
서버와 대화하는 사용자 서비스가 있다고 가정합니다.
var services = angular.module('app.services', []);
services.factory('User', function ($q, $http) {
function GET(path) {
var defer = $q.defer();
$http.get(path).success(function (data) {
defer.resolve(data);
}
return defer.promise;
}
return {
get: function (handle) {
return GET('/api/' + handle); // RETURNS A PROMISE
},
// ...
};
});
해당 서비스를 테스트해 보면 반환된 값이 어떻게 되든 상관없이 HTTP 호출만 제대로 이루어졌을 뿐입니다.
describe 'User service', ->
User = undefined
$httpBackend = undefined
beforeEach module 'app.services'
beforeEach inject ($injector) ->
User = $injector.get 'User'
$httpBackend = $injector.get '$httpBackend'
afterEach ->
$httpBackend.verifyNoOutstandingExpectation()
$httpBackend.verifyNoOutstandingRequest()
it 'should get a user', ->
$httpBackend.expectGET('/api/alice').respond { handle: 'alice' }
User.get 'alice'
$httpBackend.flush()
이제 컨트롤러 테스트에서는 HTTP에 대해 걱정할 필요가 없습니다.사용자 서비스가 실행되고 있는지 확인하고 싶을 뿐입니다.
angular.module('app.controllers')
.controller('UserCtrl', function ($scope, $routeParams, User) {
$scope.user = User.get($routeParams.handle);
});
이를 테스트하기 위해 우리는 User 서비스를 염탐합니다.
describe 'UserCtrl', () ->
User = undefined
scope = undefined
user = { handle: 'charlie', name: 'Charlie', email: 'charlie@example.com' }
beforeEach module 'app.controllers'
beforeEach inject ($injector) ->
# Spy on the user service
User = $injector.get 'User'
spyOn(User, 'get').andCallFake -> user
# Other service dependencies
$controller = $injector.get '$controller'
$routeParams = $injector.get '$routeParams'
$rootScope = $injector.get '$rootScope'
scope = $rootScope.$new();
# Set up the controller
$routeParams.handle = user.handle
UserCtrl = $controller 'UserCtrl', $scope: scope
it 'should get the user by :handle', ->
expect(User.get).toHaveBeenCalledWith 'charlie'
expect(scope.user.handle).toBe 'charlie';
약속을 해결할 필요는 없습니다.도움이 되길 바랍니다.
언급URL : https://stackoverflow.com/questions/15048132/angularjs-promise-not-being-resolved-in-unit-test
'programing' 카테고리의 다른 글
PHP Mailer vs.스위프트 메일러? (0) | 2023.09.18 |
---|---|
Yoast SEO 제목과 설명은 어디에 저장됩니까? (0) | 2023.09.18 |
하위 플롯에서 xticks를 설정하는 방법 (0) | 2023.09.18 |
여러 특성 값에서 요소를 선택하는 방법 (0) | 2023.09.18 |
UTC ISO8601 날짜를 MySQL 데이터베이스에 저장하려면 어떻게 해야 합니까? (0) | 2023.09.18 |