AngularJS가 약속하는 콜백은 Jasmine에서 트리거되지 않음JS 테스트
문제 소개
나는 Angular의 유닛 테스트를 시도하고 있다.Facebook JavaScript SDK를 랩하는 JS 서비스 FB
오브젝트; 하지만 테스트가 작동하지 않고, 그 이유를 알 수 없습니다.또한 Jasmine이 아닌 브라우저에서 실행하면 서비스 코드가 작동합니다.JS 유닛 테스트, Karma 테스트 주자와 함께 실행.
오브젝트를 통한 Angular 약속을 사용하여 비동기 방식을 테스트하고 있습니다.Jasmine 1.3.1 비동기 테스트 방법을 사용하여 비동기적으로 실행되도록 테스트를 설정했지만 함수는 돌아오지 않습니다(아래 테스트 코드 참조). 5초 후에 타임아웃됩니다(Karma는 아직 Jasmine 2.0 비동기 테스트 API를 지원하지 않습니다).
그 이유는 ''가then()
console.log()
을 해 두었습니다만, 제가 이죠.$scope.$apply()
하고 Angular를 .then()
틀릴 도 있어.내가 틀릴 수도 있어
테스트 실행 시 발생하는 오류 출력은 다음과 같습니다.
Chrome 32.0.1700 (Mac OS X 10.9.1) service Facebook should return false
if user is not logged into Facebook FAILED
timeout: timed out after 5000 msec waiting for something to happen
Chrome 32.0.1700 (Mac OS X 10.9.1):
Executed 6 of 6 (1 FAILED) (5.722 secs / 5.574 secs)
코드
이것은 서비스에 대한 유닛테스트입니다(지금까지 발견한 내용을 설명하는 인라인 코멘트 참조).
'use strict';
describe('service', function () {
beforeEach(module('app.services'));
describe('Facebook', function () {
it('should return false if user is not logged into Facebook', function () {
// Provide a fake version of the Facebook JavaScript SDK `FB` object:
module(function ($provide) {
$provide.value('fbsdk', {
getLoginStatus: function (callback) { return callback({}); },
init: function () {}
});
});
var done = false;
var userLoggedIn = false;
runs(function () {
inject(function (Facebook, $rootScope) {
Facebook.getUserLoginStatus($rootScope)
// This `then()` callback never runs, even after I call
// `$scope.$apply()` in the service :(
.then(function (data) {
console.log("Found data!");
userLoggedIn = data;
})
.finally(function () {
console.log("Setting `done`...");
done = true;
});
});
});
// This just times-out after 5 seconds because `done` is never
// updated to `true` in the `then()` method above :(
waitsFor(function () {
return done;
});
runs(function () {
expect(userLoggedIn).toEqual(false);
});
}); // it()
}); // Facebook spec
}); // Service module spec
테스트 중인 Angular 서비스는 다음과 같습니다(지금까지 발견한 내용을 설명하는 인라인 댓글 참조).
'use strict';
angular.module('app.services', [])
.value('fbsdk', window.FB)
.factory('Facebook', ['fbsdk', '$q', function (FB, $q) {
FB.init({
appId: 'xxxxxxxxxxxxxxx',
cookie: false,
status: false,
xfbml: false
});
function getUserLoginStatus ($scope) {
var deferred = $q.defer();
// This is where the deferred promise is resolved. Notice that I call
// `$scope.$apply()` at the end to let Angular know to trigger the
// `then()` callback in the caller of `getUserLoginStatus()`.
FB.getLoginStatus(function (response) {
if (response.authResponse) {
deferred.resolve(true);
} else {
deferred.resolve(false)
}
$scope.$apply(); // <-- Tell Angular to trigger `then()`.
});
return deferred.promise;
}
return {
getUserLoginStatus: getUserLoginStatus
};
}]);
자원.
다음은 이 문제를 해결하기 위해 이미 살펴본 다른 리소스 목록입니다.
-
사용 및 약속을 하는 코드 합니다(「Angular」의 해 주세요).
$scope.$apply()
하여 를 트리거해야 .then()
★★★★★★★★★★★★★★★★★★」 Jasmine 비동기 테스트 예시
- 재스민.Async: Jasmine을 사용한 비동기 테스트 감소
Jasmine 2.0.0을 사용한 비동기 Javascript 테스트
여기에서는 Jasmine 1.3.1 비동기 방식을 사용하여 Promise 패턴을 구현하는 개체를 테스트하는 예를 보여 줍니다.이 패턴은 Jasmine 1.3.1 비동기 테스트 문서에서 직접 나온 예를 본뜬 자체 테스트에서 사용한 패턴과는 약간 다릅니다.
Stack Overflow 응답
요약
Facebook JavaScript SDK에는 이미 다음과 같은 다른 Angular 라이브러리가 있다는 것을 알고 있습니다.
Angular 서비스 작성 방법을 직접 배우고 싶었기 때문에 지금은 사용할 생각이 없습니다.그러니 다른 사람의 것을 사용하라고 제안하지 말고 내 코드의 문제를 해결하는 데 도움을 주는 것으로만 답변을 제한해 주세요.
그래서 말인데, 내 테스트가 왜 안 되는지 아는 사람 있어?
TL;DR
★★$rootScope.$digest()
을 사용하다
it('should return false if user is not logged into Facebook', function () {
...
var userLoggedIn;
inject(function (Facebook, $rootScope) {
Facebook.getUserLoginStatus($rootScope).then(function (data) {
console.log("Found data!");
userLoggedIn = data;
});
$rootScope.$digest(); // <-- This will resolve the promise created above
expect(userLoggedIn).toEqual(false);
});
});
여기 플런커.
주의: 삭제했습니다.run()
★★★★★★★★★★★★★★★★★」wait()
(실제 비동기 콜은 실행되지 않기 때문에) 콜이 필요 없기 때문입니다.
긴 설명
현재 상황은 다음과 같습니다.했을 때getUserLoginStatus()
으로는 「」를 실행하고 있습니다.FB.getLoginStatus()
콜백을 즉시 실행할 수 있습니다.정확히 그렇게 하도록 조롱해 왔기 때문입니다.하지만 당신의$scope.$apply()
콜은 콜백 내에 있기 때문에 콜백 전에 실행됩니다..then()
이치노 그 이후로then()
새로운 약속이 생성되고 해당 약속이 해결되려면 새로운 다이제스트가 필요합니다.
다음 두 가지 이유 중 하나로 인해 브라우저에서는 이 문제가 발생하지 않는다고 생각합니다.
FB.getLoginStatus()
을 즉시 에 임의의 콜백이 호출됩니다.then()
먼저 되다, 또는 이 먼저 실행되다- 애플리케이션의 다른 것에 의해서, 새로운 다이제스트 사이클이 트리거 됩니다.
마지막으로 테스트 내에서 명시적으로든 아니든 약속을 작성하면 그 약속이 해결되기 위해서는 어느 시점에서 다이제스트 사이클을 트리거해야 합니다.
'use strict';
describe('service: Facebook', function () {
var rootScope, fb;
beforeEach(module('app.services'));
// Inject $rootScope here...
beforeEach(inject(function($rootScope, Facebook){
rootScope = $rootScope;
fb = Facebook;
}));
// And run your apply here
afterEach(function(){
rootScope.$apply();
});
it('should return false if user is not logged into Facebook', function () {
// Provide a fake version of the Facebook JavaScript SDK `FB` object:
module(function ($provide) {
$provide.value('fbsdk', {
getLoginStatus: function (callback) { return callback({}); },
init: function () {}
});
});
fb.getUserLoginStatus($rootScope).then(function (data) {
console.log("Found data!");
expect(data).toBeFalsy(); // user is not logged in
});
});
}); // Service module spec
이게 네가 원하는 걸 할 수 있을 거야.각각을 사용하여 rootScope를 설정하고 각각을 사용하여 응용 프로그램을 실행함으로써 사용자가 로그인하고 있는지 테스트도 쉽게 추가할 수 있습니다.
왜 코드가 작동하지 않는지에 대한 문제는 $scope를 투입하지 않았다는 것입니다.Michels의 답변은 $rootScope를 주입하고 다이제스트 사이클을 호출하기 때문에 효과가 있습니다.단, $apply()는 다이제스트 사이클을 호출하기 위한 상위 레벨이므로 다이제스트 사이클도 동작합니다.단, 서비스 자체에 다이제스트 사이클을 삽입하는 경우에만 동작합니다.
그러나 서비스에서는 $scope child가 생성되지 않으므로 $rootScope 자체를 삽입해야 합니다.제가 아는 한 컨트롤러만이 $scope를 삽입하여 $scope를 만들 수 있습니다.하지만 이것은 추측일 뿐 100% 확신할 수 없다.하지만 앱이 ng-app을 만들 때 $rootScope가 있다는 것을 알고 있기 때문에 $rootScope를 사용해 보겠습니다.
'use strict';
angular.module('app.services', [])
.value('fbsdk', window.FB)
.factory('Facebook', ['fbsdk', '$q', '$rootScope' function (FB, $q, $rootScope) { //<---No $rootScope injection
//If you want to use a child scope instead then --> var $scope = $rootScope.$new();
// Otherwise just use $rootScope
FB.init({
appId: 'xxxxxxxxxxxxxxx',
cookie: false,
status: false,
xfbml: false
});
function getUserLoginStatus ($scope) { //<--- Use of scope here, but use $rootScope instead
var deferred = $q.defer();
// This is where the deferred promise is resolved. Notice that I call
// `$scope.$apply()` at the end to let Angular know to trigger the
// `then()` callback in the caller of `getUserLoginStatus()`.
FB.getLoginStatus(function (response) {
if (response.authResponse) {
deferred.resolve(true);
} else {
deferred.resolve(false)
}
$scope.$apply(); // <-- Tell Angular to trigger `then()`. USE $rootScope instead!
});
return deferred.promise;
}
return {
getUserLoginStatus: getUserLoginStatus
};
}]);
언급URL : https://stackoverflow.com/questions/21616766/angularjs-promise-callback-not-trigged-in-jasminejs-test
'programing' 카테고리의 다른 글
Angular에서 fragment 식별자 제거JS URL(# 기호) (0) | 2023.02.20 |
---|---|
TypeError: 캐시할 수 없는 유형: 'dict' (dict가 다른 dict의 키로 사용되는 경우) (0) | 2023.02.20 |
jsfiddle의 js를 디버깅하는 방법 (0) | 2023.02.20 |
Next.js - 오류: 절대 URL만 지원됩니다. (0) | 2023.02.20 |
React 확인란이 onChange를 전송하지 않음 (0) | 2023.02.20 |