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...
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.