17

I have the following controller ViewMeetingCtrl.js

(function () {
    'use strict';
    angular.module('MyApp').controller('ViewMeetingCtrl', ViewMeetingCtrl);

    ViewMeetingCtrl.$inject = ['$scope', '$state', '$http', '$translate', 'notificationService', 'meetingService', '$modal', 'meeting', 'attachmentService'];

    function ViewMeetingCtrl($scope, $state, $http, $translate, notificationService, meetingService, $modal, meeting, attachmentService) {
        $scope.meeting = meeting; 

        $scope.cancelMeeting = cancelMeeting;

        function cancelMeeting(meetingId, companyId) {
            meetingService.sendCancelNotices(companyId, meetingId)
                .success(function () {
                    $state.go('company.view');
                });
        }      
    }
})();

I was able to succussfully invoke the spyOn for cancelMeeting() but not with the calling of sendCancelNotices method. What i want to do is , i want to test that whenever cancelMeeting() gets called , it calls sendCancelNotices() method . I know that i should go with createSpy method to do this . But i am not sure how to do it .

Below is the test case ViewMeetingCtrlSpec.js

describe('ViewMeetingCtrl CreateSpy --> Spying --> cancelMeeting', function () {
        var $rootScope, scope, $controller , $q  ;


        var sendCancelNoticesSpy = jasmine.createSpy('sendCancelNoticesSpy');


        beforeEach(angular.mock.module('MyApp'));

        beforeEach(inject(function ($rootScope, $controller ) {
            scope = $rootScope.$new();
            createController = function() {
                return $controller('ViewMeetingCtrl', {
                $scope: scope,
                meeting : {}
                }); 
            };
            var controller = new createController();
        }));

        it("tracks that the cancelMeeting spy was called", function() {
            //some assertion
        });

});
Malik
  • 3,520
  • 7
  • 29
  • 42
  • 2
    actually you just need to use the spyOn method like spyOn(service,'method') http://jasmine.github.io/2.0/introduction.html that will invoque the original method after performing the spy related operations – Dayan Moreno Leon Jun 05 '15 at 04:50

1 Answers1

29
describe('ViewMeetingCtrl', function () {

    var scope, meetingService;

    beforeEach(angular.mock.module('MyApp'));

    beforeEach(inject(function ($rootScope, $controller, _meetingService_) {
        scope = $rootScope.$new();
        meetingService = _meetingService_;
        $controller('ViewMeetingCtrl', {
            $scope: scope,
            meeting : {}
        }); 
    }));

    it('should send cancel notices whan cancelMeeting is called', function() {
        var fakeHttpPromise = {
            success: function() {}
        };
        spyOn(meetingService, 'sendCancelNotices').andReturn(fakeHttpPromise);

        scope.cancelMeeting('foo', 'bar');

        expect(meetingService.sendCancelNotices).toHaveBeenCalledWith('bar', 'foo');
    });

});

I would encourage you to stop relying of HTTP promises being returned from services. Instead, just consider the service returns a promise. Those are easier to mock, and won't force you to rewrite your controller code when you don't return HTTP promises anymore.

In your controller:

    function cancelMeeting(meetingId, companyId) {
        meetingService.sendCancelNotices(companyId, meetingId)
            .then(function () {
                $state.go('company.view');
            });
    } 

In your test:

        var fakePromise = $q.when();
        spyOn(meetingService, 'sendCancelNotices')and.returnValue(fakePromise);

        scope.cancelMeeting('foo', 'bar');
        expect(meetingService.sendCancelNotices).toHaveBeenCalledWith('bar', 'foo');
Malik
  • 3,520
  • 7
  • 29
  • 42
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 1
    thank you for your answer . that exactly what i wanted . however there was a small change needed to be done (and.returnValue()) since i am using jasmine 2.0 – Malik Jun 08 '15 at 07:57
  • In above code we are calling spyOn (and we can call createSpyObj to get entire mocked object)on real service instance of MeetingService.Correct?In our code we are just creating a spy object on a String object where value of the String is same as name of the service. We write something : beforeEach(function(){ $provide.factory("meetingService",function(){ meetingServiceMock = jasmine.createSpyObj("meetingService",['sendCancelNotices']); return meetingServiceMock;}); and then use this meetingServiceMock everywhere in "it" blocks. Even if it works correctly..do not think it makes sense does it? – Shailesh Vaishampayan Nov 25 '15 at 20:41
  • It does make sense, but it's more work, and you always have the risk of creating a mock service that doesn't have the same methods as the actual service. – JB Nizet Nov 25 '15 at 23:15
  • OK.it makes sense because you can still record and verify the spy calls. And it's more work because I have to explicitly retrieve it using one $provide per service I have to mock. Correct? – Shailesh Vaishampayan Nov 26 '15 at 04:07
  • And why is it a risk ? If I am explicitly listing which methods spies I want in the mocked object which I have to do anyway while creating a mock even if its the actual instance? Can you please explain. One risk is that I cannot use call through on the spy as the instance on which I am creating spies and mock is not actual service instance but a string instance. Am I correct in this assumption? – Shailesh Vaishampayan Nov 26 '15 at 04:11
  • 2
    It's a risk because your tests can pass even though the component they test uses methods that only exist in the mock, and not in the actual service. – JB Nizet Nov 26 '15 at 06:54
  • Yes. but I am including methods while creating mock that exist only in the service. Is my assumption in last comment correct about callThrough? – Shailesh Vaishampayan Nov 26 '15 at 11:24
  • Can you also please help me with following question :http://stackoverflow.com/questions/33938552/jasmine-unit-test-fails-with-unexpected-request – Shailesh Vaishampayan Nov 26 '15 at 12:23
  • Note that if you're reading this in 2020 or later, the jasmine functions have changed. Should be `spyOn(meeetingService, 'sendCancelNotices').and.returnValue(fakeHttpPromise)` – Tony Brasunas Aug 18 '20 at 16:53