web-dev-qa-db-ja.com

Karma / Jasmineを使用したmodalInstanceコントローラーの単体テスト

編集:この投稿の最後にクイック&ダーティソリューション

ファイルを分割したことを除いて、Webサイトで説明されているのと同じ方法で、AngularUI-Bootstrapのモーダルウィンドウを使用しています。したがって、私は持っています:

CallingController.js:

$scope.delete = function () {
    if ($scope.selected.length > 0) {
        // [...]
        // preparing data
        // [...]
        var modalInstance = $modal.open({
            templateUrl: 'views/modalView.html',
            controller: 'modalCtrl',
            resolve: {
                itemArray: function () {
                    return $scope.selected;
                }
            }
        });
        modalInstance.result.then(function (confirm) {
            if (confirm === true) {
                // [...]
                // treat
                // [...]
            }
        });
    }
};

modalController.js:

myAppControllers.controller('modalCtrl',
    function ($scope, $modalInstance, itemArray) {

        $scope.accept = function () {
            $modalInstance.close(true);
        };

        $scope.reject = function () {
            $modalInstance.close(false);
        };

        $scope.itemArray = itemArray;

    });

karmaでこのコードをテストすると(ui-bootstrap-tpls.min.jsファイルがkarma構成ファイルに読み込まれます)、私は次のエラー:エラー:[$ injector:unpr] [ http://errors.angularjs.org/1.2.15-build.2389+sha.c5f2f58/ $ injector/unpr?p0 =%24modalInstanceProvider%20%3C-%20%24modalInstance] 1 at at Error(native)、つまりジャスミンは$ modalInstanceのプロバイダーを見つけます。

私はまだこのコントローラーでテストしていませんが、ここに私のジャスミンテストファイルがあります:

testModalController.js:

describe('Controller: modalCtrl', function () {

    beforeEach(module('myApp'));

    var Ctrl;
    var scope;

    // Initialize the controller and a mock scope
    beforeEach(inject(
        function ($controller, $rootScope) {
            scope = $rootScope.$new();

            Ctrl = $controller('modalCtrl', { $scope: scope });
        })
    );

    describe('Initial state', function () {
        it('should instantiate the controller properly', function () {
            expect(Ctrl).not.toBeUndefined();
        });

        it('should initialize its values properly', function () {

        });
    });

});

この問題に関する手がかりはありますか?私が使用(およびテスト)する最初の「外部」モジュールではありません。他のモジュールと同じことをしましたが、今回は機能せず、理由がわかりません。

======================================== =

編集:迅速でおそらく汚い解決策:

さて、ジャスミンのコントローラーのインスタンス化におけるスコープのモック方法に基づいて、私はどのように私の問題を「解決」できるかを考え出しましたが、おそらくかなり汚いので、あなたが意図したことを行うより良い方法を見つけたらコメントしてください。

testModalController.js:

describe('Controller: modalCtrl', function () {

    beforeEach(module('myApp'));

    var Ctrl;
    var scope;
    var modalInstance;

    // Initialize the controller and a mock scope
    beforeEach(inject(
        function ($controller, $rootScope, _$modal_) {
            scope = $rootScope.$new();
            modalInstance = _$modal_.open({
                templateUrl: 'views/modalView.html'
            });

            Ctrl = $controller('modalCtrl', {
                $scope: scope,
                $modalInstance: modalInstance,
                itemArray: function () { return ['a', 'b', 'c']; }
            });
        })
    );

    describe('Initial state', function () {
        it('should instantiate the controller properly', function () {
            expect(Ctrl).not.toBeUndefined();
        });

        it('should initialize its values properly', function () {

        });
    });

});

この方法では、Jasmineはプロバイダーをもう検索しません。これは、これらのプロバイダーを必要とするはずのアイテムを既に挿入しているためです。うまくいきますが、もっと良い方法でできると思います...

25
Manyuuz

モックmodalおよびmodalInstanceオブジェクトを作成し、それらがコントローラーコードによって呼び出されたことを確認することでこれを解決しています。 modalmodalInstanceはサードパーティのライブラリの一部であるため、それらが適切に動作することをテストするのは私たちの責任ではありません。むしろ、ライブラリを呼び出すコードが正常に動作することをテストするのは私たちの責任です。

あなたの例を使用して:

describe('Controller: modalCtrl', function () {

  beforeEach(module('myApp'));

  var Ctrl;
  var scope;
  var modalInstance;

  // Initialize the controller and a mock scope
  beforeEach(inject(
    function ($controller, $rootScope) {     // Don't bother injecting a 'real' modal
      scope = $rootScope.$new();
      modalInstance = {                    // Create a mock object using spies
        close: jasmine.createSpy('modalInstance.close'),
        dismiss: jasmine.createSpy('modalInstance.dismiss'),
        result: {
          then: jasmine.createSpy('modalInstance.result.then')
        }
      };
      Ctrl = $controller('modalCtrl', {
        $scope: scope,
        $modalInstance: modalInstance,
        itemArray: function () { return ['a', 'b', 'c']; }
      });
    })
  );

  describe('Initial state', function () {
    it('should instantiate the controller properly', function () {
      expect(Ctrl).not.toBeUndefined();
    });

    it('should close the modal with result "true" when accepted', function () {
      scope.accept();
      expect(modalInstance.close).toHaveBeenCalledWith(true);
    });

    it('should close the modal with result "false" when rejected', function () {
      scope.reject();
      expect(modalInstance.close).toHaveBeenCalledWith(false);
    });
  });
});

このように、Angular-UIオブジェクトへの依存は実際には必要ありません。ユニットテストはNiceで隔離されています。

63
Tom Spencer

の代わりに:

_modalInstance = {                    // Create a mock object using spies
  close: jasmine.createSpy('modalInstance.close'),
  dismiss: jasmine.createSpy('modalInstance.dismiss'),
  result: {
    then: jasmine.createSpy('modalInstance.result.then')
  }
};_

これは次のように記述できます。

modalInstance = jasmine.createSpyObj('modalInstance', ['close', 'dismiss', 'result.then']);

また、現在$ uibModalInstanceである$ modalInstanceはないため、上記のすべての「modalInstance」は「uibModalInstance」に置き換える必要があります。

6
Jason Rice

fiznoolの回答に対して+1。正しいため、選択する必要があります。

ただし、ここで説明する方法では維持できません。

これは角張っているため、使用することをお勧めします。

angular.module('...').service('$modalInstance', function(){
   ... define spies and such 
})

コードをはるかにモジュール化された汎用的なものにします。上記のコンテンツを含むファイルをspecの下に追加し、karma.conf

特定のテストでのみロードされるようにする場合は、一意のモジュール名を付けて、modulebeforeEach呼び出しに追加するだけです。

2
guy mograbi