I'm having problems trying to write a jasmine unit test for an Angular-Bootstrap $modal
. The exact error is
Expected spy open to have been called with [ { templateUrl : '/n/views/consent.html', controller : 'W2ConsentModal as w2modal', resolve : { employee : Function }, size : 'lg' } ] but actual calls were [ { templateUrl : '/n/views/consent.html', controller : 'W2ConsentModal as w2modal', resolve : { employee : Function }, size : 'lg' } ]
The expected and actual modal options object are the same. What is going on?
Controller
(function () {
'use strict';
angular
.module('app')
.controller('W2History', W2History);
W2History.$inject = ['$scope', '$modal', 'w2Service'];
function W2History($scope, $modal, w2Service) {
/* jshint validthis:true */
var vm = this;
vm.showModal = showModal;
function showModal(employee) {
var modalInstance = $modal.open({
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: function () {
return employee;
}
},
size: 'lg'
});
modalInstance.result.then(function (didConsent) {
// code omitted
});
}
}
})();
Test
describe('W2History controller', function () {
var controller, scope, modal;
var fakeModal = {
result: {
then: function (confirmCallback, cancelCallback) {
//Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog
this.confirmCallBack = confirmCallback;
this.cancelCallback = cancelCallback;
}
},
close: function (item) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.result.confirmCallBack(item);
},
dismiss: function (type) {
//The user clicked cancel on the modal dialog, call the stored cancel callback
this.result.cancelCallback(type);
}
};
var modalOptions = {
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: function () {
return employee;
}
},
size: 'lg'
};
beforeEach(function () {
module('app');
inject(function (_$controller_, _$rootScope_, _$modal_) {
scope = _$rootScope_.$new();
modal = _$modal_;
spyOn(modal, 'open').and.returnValue(fakeModal);
controller = _$controller_('W2History', {
$scope: scope,
$modal: modal,
w2Service: w2Srvc
});
});
});
it('Should correctly show the W2 consent modal', function () {
var employee = terminatedaccessMocks.getCurrentUserInfo();
controller.showModal(employee);
expect(modal.open).toHaveBeenCalledWith(modalOptions);
});
});
Try this:
PLUNK
Explanation:
We should not expect the actual
resolve.employee
to be the same with the fakeresolve.employee
becauseresolve.employee
is a function which returns an employee (in this case the employee is captured in closure). The function could be the same but at runtime the returned objects could be different.The reason your test is failing is the way javascript compares functions. Take a look at this fiddle. Anyway, I don't care about this because we should not expect function implementations. What we do care about in this case is the
resolve.employee
returns the same object as we pass in:So the solution here is: We expect everything except for the
resolve.employee
:Check the
resolve.employee
separately by capturing it first:This is a pass by reference vs pass by value issue. The
resolve.employee
anonymous function used in$modal.open
:is not the same (by reference) as the
resolve.employee
anonymous function in your test:Your test should be:
If it's essential that the resolve function be tested, you should expose it somewhere where you can get a reference to the same function in your tests.
I have come across the same scenario. I have come across the problem with the below given solution
Hope this may help if you want a quick fix.
I am not sure if this will help you now, but when you spy on something you can get the argument that is passed to the $uibModal.open spy, you can then call that function to test that it returns what is in the resolve method.
***** my code is using typescript, but this works for me.**