Unit testing angular-bootstrap $ modal

I'm having trouble trying to write a jasmine unit test for Angular-Bootstrap $modal . Exact error 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 object of modal options is the same. What's happening?

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); }); }); 
+2
javascript angularjs unit-testing jasmine angular-ui
source share
4 answers

Try the following:

 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: jasmine.any(Function) }, size: 'lg' }; var actualOptions; beforeEach(function () { module('plunker'); inject(function (_$controller_, _$rootScope_, _$modal_) { scope = _$rootScope_.$new(); modal = _$modal_; spyOn(modal, 'open').and.callFake(function(options){ actualOptions = options; return fakeModal; }); controller = _$controller_('W2History', { $scope: scope, $modal: modal }); }); }); it('Should correctly show the W2 consent modal', function () { var employee = { name : "test"}; controller.showModal(employee); expect(modal.open).toHaveBeenCalledWith(modalOptions); expect(actualOptions.resolve.employee()).toEqual(employee); }); }); 

Plunk

Explanation

We should not expect the actual resolve.employee be the same as the fake resolve.employee , because resolve.employee is a function that returns the employee (in this case, the employee is delayed in closing). The function may be the same, but at run time, the returned objects may be different.

The reason your test fails is because javascript compares functions. Take a look at the fiddle . In any case, this does not bother me, because we should not expect the implementation of functions . In this case, we take care that resolve.employee returns the same object that we are passing:

 expect(actualOptions.resolve.employee()).toEqual(employee); 

So, the solution is here: We expect everything except resolve.employee :

 var modalOptions = { templateUrl: '/n/views/consent.html', controller: 'W2ConsentModal as w2modal', resolve: { employee: jasmine.any(Function) //don't care about the function as we check it separately. }, size: 'lg' }; expect(modal.open).toHaveBeenCalledWith(modalOptions); 

Check resolve.employee separately by first pinning it:

 var actualOptions; spyOn(modal, 'open').and.callFake(function(options){ actualOptions = options; //capture the actual options return fakeModal; }); expect(actualOptions.resolve.employee()).toEqual(employee); //Check the returned employee is actually the one we pass in. 
+13
source share

This is pass by reference vs pass by value . The anonymous resolve.employee function used in $modal.open :

 var modalInstance = $modal.open({ templateUrl: '/n/views/consent.html', controller: 'W2ConsentModal as w2modal', resolve: { employee: function () { return employee; } }, size: 'lg' }); 

is not the same (by reference) as the anonymous resolve.employee function in your test:

 var modalOptions = { templateUrl: '/n/views/consent.html', controller: 'W2ConsentModal as w2modal', resolve: { employee: function () { return employee; } }, size: 'lg' }; 

Your test should be:

 resolve: { employee: jasmine.any(Function) } 

If you want the permission function to be tested, you should open it somewhere where you can get a link to the same function in your tests.

+1
source share

I'm not sure if this will help you now, but when you look at something, you can get an argument that is passed to the $ uibModal.open spy, then you can call this function to check that it returns what is in resolution method.

 it('expect resolve to be have metadataid that will return 9999', () => { spyOn($uibModal, 'open'); //add test code here that will call the $uibModal.open var spy = <jasmine.Spy>$uibModal.open; var args = spy.calls.argsFor(0); expect(args[0].resolve.metadataId()).toEqual(9999); }); 

***** my code uses typescript, but this works for me. **

+1
source share

I came across the same scenario. I ran into a problem with the solution below.

 //Function to open export modal scope.openExportModal(); expect( uibModal.open ).toHaveBeenCalledWith(options); expect( uibModal.open.calls.mostRecent().args[0].resolve.modalData() ).toEqual(modalData); 

Hope this helps if you want a quick fix.

0
source share

All Articles