Unit testing $modal with Jasmine

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.

Answers:

Answer

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

The modal show is async. I updated your fiddle at http://jsfiddle.net/jwom7ns2/1/.

Change the following portion:

it('should show the modal', function (done) {
    var modalSpy = spyOn(modal(), 'show');

    scope.makeItFoo();

    setTimeout(function() {
        expect(modalSpy).toHaveBeenCalled();
        done();
    });

});

The timeout wrapper waits for the digest to happen when the modal show occurs.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.