web-dev-qa-db-ja.com

Angularjsの単体テストのpromiseベースのコード

私はAngularjsで約束ベースのコードをテストするのに苦労しています。

コントローラーに次のコードがあります。

    $scope.markAsDone = function(taskId) {
        tasksService.removeAndGetNext(taskId).then(function(nextTask) {
            goTo(nextTask);
        })
    };

    function goTo(nextTask) {
        $location.path(...);
    }

次のケースのユニットテストを行います。

  • markAsDoneが呼び出されると、tasksService.removeAndGetNextを呼び出す必要があります
  • tasksService.removeAndGetNextが完了したら、場所を変更する必要があります(goToを呼び出します)

これらの2つのケースを個別にテストする簡単な方法はないと思われます。

最初のものをテストするために私がしたことは:

var noopPromise= {then: function() {}}
spyOn(tasksService, 'removeAndGetNext').andReturn(noopPromise);

次に、2番目のケースをテストするために、常にresolvedになる別の偽のプロミスを作成する必要があります。それはすべて非常に面倒であり、多くの定型コードです。

そのようなことをテストする他の方法はありますか?または、私のデザインは匂いがしますか?

67

それでもサービスをモックしてプロミスを返す必要がありますが、代わりに実際のプロミスを使用する必要があるため、その機能を実装する必要はありません。 beforeEachを使用して、すでに満たされた約束を作成し、常に解決する必要がある場合はサービスをモックします。

var $rootScope;

beforeEach(inject(function(_$rootScope_, $q) {
  $rootScope = _$rootScope_;

  var deferred = $q.defer();
  deferred.resolve('somevalue'); //  always resolved, you can do it from your spec

  // jasmine 2.0
  spyOn(tasksService, 'removeAndGetNext').and.returnValue(deferred.promise); 

  // jasmine 1.3
  //spyOn(tasksService, 'removeAndGetNext').andReturn(deferred.promise); 

}));

異なる値で各itブロックで解決したい場合は、遅延変数をローカル変数に公開し、仕様で解決します。

もちろん、テストはそのままにしておきますが、どのように機能するかを示すための非常に簡単な仕様を次に示します。

it ('should test receive the fulfilled promise', function() {
  var result;

  tasksService.removeAndGetNext().then(function(returnFromPromise) {
    result = returnFromPromise;
  });

  $rootScope.$apply(); // promises are resolved/dispatched only on next $digest cycle
  expect(result).toBe('somevalue');
});
107
Caio Cunha

別のアプローチは、私がテストしていたコントローラーから直接取り出した次のものです。

var create_mock_promise_resolves = function (data) {
    return { then: function (resolve) { return resolve(data); };
};

var create_mock_promise_rejects = function (data) {
    return { then: function (resolve, reject) { if (typeof reject === 'function') { return resolve(data); } };
};

var create_noop_promise = function (data) {
    return { then: function () { return this; } };
};
4
yangmillstheory

さらに別のオプションとして、$digestのドロップイン置換としてQライブラリ( https://github.com/kriskowal/q )を使用して$qbyを呼び出す必要を回避できます。

beforeEach(function () {
    module('Module', function ($provide) {
        $provide.value('$q', Q); 
    });
});

このようにして、$ digestサイクル以外でプロミスを解決/拒否できます。

0
Michael