Подтвердить что ты не робот

Тестирование AngularJS promises в Jasmine 2.0

Я пытаюсь обернуть голову вокруг Jasmine 2.0 и AngularJS promises. Я знаю, что:

Как я могу проверить AngularJS promises, используя новый синтаксис асинхронизации в Jasmine 2.0?

4b9b3361

Ответ 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. Что сложно сказать, хорошо это или плохо.

Однако в этом случае тест выглядит более разумным и намного проще.