Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
703 views
in Technique[技术] by (71.8m points)

angularjs - Unit testing a modalInstance controller with Karma / Jasmine

EDIT : Quick & Dirty solution at the end of this post

I am using a modal window from AngularUI-Bootstrap in the same way that it is explained on the website, except that I splitted files. Therefore I have :

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;

    });

and when I test this code with Karma (with the ui-bootstrap-tpls.min.js file loaded in the karma configuration file), I get the following error : Error: [$injector:unpr] [http://errors.angularjs.org/1.2.15-build.2389+sha.c5f2f58/$injector/unpr?p0=%24modalInstanceProvider%20%3C-%20%24modalInstance]1 at Error (native), meaning that jasmine doesn't manage to find the provider for $modalInstance.

I do not even test stuff on this controller, not yet, but here is my jasmine test file :

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 () {

        });
    });

});

Have you got any clue about this problem ? It's not the first "external" module that I use (and test), and I did the same stuff than for the others, except that this time it doesn't work and I have no idea why.

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

EDIT: Quick & probably dirty solution :

Okay, so based on the scope mocking method in the controller instantiation of Jasmine, I figured out how I could "solve" my problem, but it's probably quite dirty, so feel free to comment if you find a better way to do what I intend.

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 () {

        });
    });

});

This way, Jasmine doesn't search for providers anymore, because you already injected the items that are supposed to be needing those providers. It works, but I believe it could be done in a better way...

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I am solving this by just creating mock modal and modalInstance objects and verifying that they have been called by my controller code. Since modal and modalInstance are part of a third party library, it's not our responsibility to test that they work properly - rather, it's our responsibility to test that our code which calls the library is working ok.

Using your example:

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);
    });
  });
});

This way, we don't really need any dependency on the Angular-UI objects and our unit tests are nice and isolated.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...