Тестирование AngularJS promises в Jasmine 2.0
Я пытаюсь обернуть голову вокруг Jasmine 2.0 и AngularJS promises. Я знаю, что:
Как я могу проверить AngularJS promises, используя новый синтаксис асинхронизации в Jasmine 2.0?
Ответы
Ответ 1
После вашего вызова promise.resolve()
:
- Вызвать
$timeout.flush()
. Это заставит цикл дайджеста и распространить обещание.
- Вызов
done()
. Это говорит Жасмин, что асинхронные тесты завершили
Вот пример ( Demo on Plunker):
describe('AngularJS promises and Jasmine 2.0', function() {
var $q, $timeout;
beforeEach(inject(function(_$q_, _$timeout_) {
// Set `$q` and `$timeout` before tests run
$q = _$q_;
$timeout = _$timeout_;
}));
// Putting `done` as argument allows async testing
it('Demonstrates asynchronous testing', function(done) {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve('I told you I would come!');
}, 1000); // This won't actually wait for 1 second.
// `$timeout.flush()` will force it to execute.
deferred.promise.then(function(value) {
// Tests set within `then` function of promise
expect(value).toBe('I told you I would come!');
})
// IMPORTANT: `done` must be called after promise is resolved
.finally(done);
$timeout.flush(); // Force digest cycle to resolve promises
});
});
Ответ 2
Для меня $timeout.flush()
работал не очень хорошо, но у меня несколько асинхронных вызовов в моей спецификации. Я нашел $rootScope.$apply()
как метод принудительного digest
для каждого асинхронного вызова.
describe('AngularJS promises and Jasmine 2.0', function () {
beforeEach(inject(function (_$q_, _$timeout_, _$rootScope_) {
$q = _$q_
$timeout = _$timeout_
$rootScope = _$rootScope_
}))
it('demonstrates asynchronous testing', function (done) {
var defer = $q.defer()
Async.call()
.then(function (response) {
// Do something
var d = $q.defer()
Async.call()
.then(function (response) {
d.resolve(response)
$rootScope.$apply() // Call the first digest
})
return d.promise
})
.then(function (response) {
// Do something after the first digest
Async.call()
.then(function (response) {
defer.resolve(response) // The original defer
$rootScope.$apply() // Call the second digest
})
})
defer.promise.then(function(value) {
// Do something after the second digest
expect(value).toBe('I told you I would come!')
})
.finally(done)
if($timeout.verifyNoPendingTasks())
$timeout.flush()
})
})
Это похоже на цепочку асинхронных вызовов. Надеюсь, это поможет разговору.
Отношения
Ответ 3
Этот ответ не добавит ничего нового к приведенному выше, он предназначен только для более подробного формулирования ответа, поскольку он работал на меня. Когда я столкнулся с проблемой, описанной в вопросе выше, я потратил много времени, пытаясь найти способ убедиться, что все promises успели закончить и все утверждения были утверждены.
В моем случае у меня была цепочка promises, и после каждого из них мне нужно убедиться, что результаты соответствуют моим ожиданиям. Я не создавал никаких обещаний с помощью deferred
, я скорее использовал существующие.
Итак, дело в том, что $timeout.flush()
было вполне достаточно для меня. Мой рабочий тест выглядит следующим образом:
describe("Plain command without side-effects", function() {
it("All usecases", inject(function($timeout) {
console.log("All together");
expect(state.number).toEqual(1);
cmdHistory
.execute(increaseState, decreaseState)
.then(function() {
console.log("Execute works");
expect(state.number).toEqual(2);
return cmdHistory.redo(); // can't redo, nothing undone
})
.then(function() {
console.log("Redo would not work");
expect(state.number).toEqual(2);
return cmdHistory.undo();
})
.then(function() {
console.log("Undo undoes");
expect(state.number).toEqual(1);
return cmdHistory.undo();
})
.then(function() {
console.log("Next undo does nothing");
expect(state.number).toEqual(1);
return cmdHistory.redo(); // but still able to redo
})
.then(function() {
console.log("And redo redoes neatly");
expect(state.number).toEqual(2);
});
$timeout.flush();
}));
Этот тест предназначен для того, чтобы убедиться, что объект commandHistory работает нормально, он имеет действия: execute
и unExecute
и три метода: execute
, undo
, redo
, все из которых возвращают promises.
Без $timeout.flush()
все, что у меня было на выходе журнала, было All together
, и больше никаких сообщений журнала. Добавление $timeout.flush()
фиксировало все, и теперь у меня есть все отображаемые сообщения и все выполненные утверждения
UPDATE
Еще один вариант: вы можете написать свой тестовый пакет без привязки promises с помощью then
, но просто помыть после каждого обещания, чтобы убедиться, что он завершен:
it("All usecases 2", inject(function($timeout) {
console.log("All usecases 2");
expect(state.number).toEqual(1);
console.log("Execute works");
cmdHistory.execute(increaseState, decreaseState);
$timeout.flush();
expect(state.number).toEqual(2);
console.log("Redo would not work");
cmdHistory.redo(); // can't redo, nothing undone
$timeout.verifyNoPendingTasks();
expect(state.number).toEqual(2);
console.log("Undo undoes");
cmdHistory.undo();
$timeout.flush();
expect(state.number).toEqual(1);
console.log("Next undo does nothing");
cmdHistory.undo();
$timeout.verifyNoPendingTasks();
expect(state.number).toEqual(1);
console.log("And redo redoes neatly");
cmdHistory.redo(); // but still able to redo
$timeout.flush();
expect(state.number).toEqual(2);
}));
Обратите внимание на тот факт, что в некоторых случаях, когда мои методы вроде undo
и redo
не возвращают обещание, я вызываю $timeout.verifyNoPendingTasks()
вместо flush
. Что сложно сказать, хорошо это или плохо.
Однако в этом случае тест выглядит более разумным и намного проще.