Как разрешить промисы в AngularJS, Jasmine 2.0, когда нет $scope для принудительного дайджеста?
-
21-12-2019 - |
Вопрос
Кажется, обещает не разрешайте в тестах Angular/Jasmine, если вы не принудительно $scope.$digest()
.Это глупо, по моему мнению, но ладно, у меня это работает там, где это применимо (контроллеры).
Ситуация, в которой я сейчас нахожусь, заключается в том, что у меня есть служба, которая меньше заботится о каких-либо областях приложения, все, что она делает, это возвращает некоторые данные с сервера, но обещание, похоже, не разрешается.
app.service('myService', function($q) {
return {
getSomething: function() {
var deferred = $q.defer();
deferred.resolve('test');
return deferred.promise;
}
}
});
describe('Method: getSomething', function() {
// In this case the expect()s are never executed
it('should get something', function(done) {
var promise = myService.getSomething();
promise.then(function(resp) {
expect(resp).toBe('test');
expect(1).toEqual(2);
});
done();
});
// This throws an error because done() is never called.
// Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
it('should get something', function(done) {
var promise = myService.getSomething();
promise.then(function(resp) {
expect(resp).toBe('test');
expect(1).toEqual(2);
done();
});
});
});
Как правильно протестировать эту функциональность?
Редактировать:Решение для справки.По-видимому, вы вынуждены внедрить и переварить $rootScope, даже если служба его не использует.
it('should get something', function($rootScope, done) {
var promise = myService.getSomething();
promise.then(function(resp) {
expect(resp).toBe('test');
});
$rootScope.$digest();
done();
});
Решение
Вам нужно сделать инъекцию $rootScope
в вашем тесте и триггере $digest
в теме.
Другие советы
Всегда есть $ rootscope, используйте его
inject(function($rootScope){
myRootScope=$rootScope;
})
....
myRootScope.$digest();
. Так что мне пришлось бороться с этим весь день.Прочитав этот пост, я тоже почувствовал, что с ответом что-то не так; оказывается, так оно и есть.Ни один из приведенных выше ответов не дает четкого объяснения относительно того, где и зачем использовать $rootScope.$digest
.Итак, вот что у меня получилось.
Во-первых, почему?Вам нужно использовать $rootScope.$digest
всякий раз, когда вы отвечаете на неугловое событие или обратный вызов.Это будет включать в себя чистые события DOM, события jQuery и другие сторонние библиотеки Promise, отличные от $q
который является частью angular.
Во-вторых, где?В вашем коде, А Не в вашем тесте.Нет необходимости делать инъекции $rootScope
в вашем тесте это необходимо только в вашем реальном сервисе angular.Вот где все вышеперечисленное не дает ясного ответа, они показывают $rootScope.$digest
как вызываемый из теста.
Я надеюсь, что это поможет следующему человеку, который придет позже, у которого будет такая же проблема.
Обновить
Я удалил этот пост вчера, когда за него проголосовали.Сегодня я продолжал сталкиваться с этой проблемой, пытаясь использовать ответы, любезно предоставленные выше.Итак, я готовлю свой ответ ценой очков репутации, и поэтому я восстанавливаю его.
Это то, что вам нужно в обработчиках событий, которые не являются угловыми, и вы используете $ q и пытаетесь протестировать с помощью Jasmine.
something.on('ready', function(err) {
$rootScope.$apply(function(){deferred.resolve()});
});
Обратите внимание, что в некоторых случаях это может потребоваться обернуть в $timeout.
something.on('ready', function(err) {
$timeout(function(){
$rootScope.$apply(function(){deferred.resolve()});
});
});
Еще одно замечание.В примерах исходной задачи вы вызываете
done
в неподходящее время.Тебе нужно позвонитьdone
внутриthen
метод (илиcatch
илиfinally
), из обещания, после того, как это разрешится.Вы вызываете его до того, как обещание будет выполнено, что вызываетit
пункт о расторжении договора.
из угловой документации.
https://docs.angularjs.org/api/ng/service/ $ q
it('should simulate promise', inject(function($q, $rootScope) {
var deferred = $q.defer();
var promise = deferred.promise;
var resolvedValue;
promise.then(function(value) { resolvedValue = value; });
expect(resolvedValue).toBeUndefined();
// Simulate resolving of promise
deferred.resolve(123);
// Note that the 'then' function does not get called synchronously.
// This is because we want the promise API to always be async, whether or not
// it got called synchronously or asynchronously.
expect(resolvedValue).toBeUndefined();
// Propagate promise resolution to 'then' functions using $apply().
$rootScope.$apply();
expect(resolvedValue).toEqual(123);
}));
.