Unit testing $modal with Jasmine

The DIMM Reaper picture The DIMM Reaper · Oct 27, 2014 · Viewed 8.1k times · Source

I have an Angular app with a controller which displays an Angular-Strap modal window during a function call. It functions correctly in Chrome, but I am at a loss getting a valid unit test working.

App module and the FooController:

var app = angular.module("app", ["mgcrea.ngStrap"]);

app.controller("FooController", function($scope, $modal) {
    var fooModal = $modal({
        title: 'Foo',
        content:'Bar',
        show: false,
        html: true,
        backdrop: 'static',
        placement: 'center'});
    
    angular.extend($scope, {
        makeItFoo: function() {
            fooModal.show();
        }
    });
});

Controller spec:

describe('FooController', function () {
    var scope, controller, modal;

    beforeEach(module('app', function ($provide) {
        // Stub out $modal service
        $provide.value('$modal', function () {
            return {
                hide: function () { },
                show: function () { }
            };
        });
    }));

    beforeEach(inject(function ($rootScope, $controller, $injector) {
        //set up a new scope and the controller for the test
        scope = $rootScope.$new();
        controller = $controller('FooController', {$scope: scope});
        modal = $injector.get('$modal');
    }));

    it('should show the modal', function () {
        var modalSpy = spyOn(modal(), 'show');
        
        scope.makeItFoo();
        
        expect(modalSpy).toHaveBeenCalled();
    });
});

Here's a fiddle as well.

I expect my call to makeItFoo() to display the modal, but Jasmine fails the test with the error Expected spy show to have been called. I've also tried setting the show property of the modal to true and not calling show() separately, and I've tried other variants of stubbing the $modal service and injecting it directly into the controller, but it ends up with the same error.

I'm using AngularJS 1.2.14, Angular-Strap 2.0.0, and Jasmine 1.3.1.

Answer

PSL picture PSL · Oct 27, 2014

Instead of doing these. Create a mock object for $modal with show and hide methods and set your expectations on them.

describe('FooController', function () {
    var scope, controller, modal;

    beforeEach(module('app'));

    beforeEach(inject(function ($rootScope, $controller) {
        //set up a new scope and the controller for the test
        scope = $rootScope.$new();
        //Create spy object
        modal = jasmine.createSpyObj('modal', ['show', 'hide']);
        //provide modal as dependency to the controller.
        controller = $controller('FooController', {$scope: scope, $modal:modal});

    }));

    it('should show the modal', function () {

        scope.makeItFoo();

        expect(modal.show).toHaveBeenCalled();
    });
});