21

How do I test that the scope is populated after the broadcast? I've searched and found a few QA's here in stackexchange, but none answered my problem. The code is working alright, just don't know how to test it. May I add that I'm a novice to testing, and especially with Jasmine.

So, here's the code:

Service CrappySvc:

update: function() {
    $rootScope.$broadcast('updatecrappy', Crappy.query());
}   

Controller GetCrappyCtrl:

  $scope.$on('updatecrappy', function(event, crap) {
    $scope.crap = crap;
  });

Jasmine:

beforeEach(inject(function($rootScope, $httpBackend, $controller, Crappy) {
  rootScope = $rootScope;
  scope = $rootScope.$new();
  Crappy = mockCrappy;

      ...
  spyOn(rootScope, '$broadcast');
      ...

  ctrl = $controller('GetCrappyCtrl', {
  $scope : scope,
  Crappy : mockCrappy
});               

}));

it('$scope.$on should have been triggered', function() {          
  rootScope.$broadcast('updatecrappy', [{id : 2, name : 'crappy'}]);
  expect(rootScope.$broadcast).toHaveBeenCalledWith('updscenes', [{id : 2, name : 'crappy'}]);

});

Stone

stonerichnau
  • 381
  • 2
  • 7
  • 15
  • I found this helpful too: http://stackoverflow.com/questions/15272414/how-can-i-test-events-in-angular – Ben Aug 16 '14 at 01:47

4 Answers4

36

You need to tell Jasmine to let the spy call the actual $broadcast function

spyOn($rootScope, '$broadcast').andCallThrough();

If you don't use andCallThrough() the spy does nothing.

Jasmine Docs

EDIT

With Jasmine 2, the syntax would be

spyOn($rootScope, '$broadcast').and.callThrough();
ivarni
  • 17,658
  • 17
  • 76
  • 92
  • can we elaborate on what this is actually doing? – Jeff Voss Sep 03 '15 at 00:28
  • @whitebox That's the thing, it's not really doing much. By default, Jasmine's spies will replace the original function that is being spied on. Calling `and.callThrough()` will add to the default behaviour and cause the spy to *also* call the original function that was being spied on. And that's really all that it does. – ivarni Sep 03 '15 at 04:48
  • you don't need the and.callThrough() in this case if you are just asserting the broadcast was called – Ryan M Mar 08 '16 at 18:35
  • does mocha have anything like .callThrough – Winnemucca Apr 11 '16 at 17:22
  • As far as I know mocha does not come with its own spy-functionality so that would depend on which library you use for mocking. – ivarni Apr 12 '16 at 04:36
  • This doesn't really test $on. I have the following in my controller `$scope.$on('myevent', myfunction)`. I want to spy on `myfunction` and see that it gets called. I have this `spyOn($scope, 'myfunction'); $rootScope.$broadcast('myevent'); expect($scope.myfunction).toHaveBeenCalled();` All I get is a screen full of jasmine stack trace with no real error message. – HisDivineShadow Feb 20 '17 at 17:13
0

If want to fake the returnValue from $scope.$broadcast() you can do something like these two cases:

Case 1:

scope.$broadcast('TEST_CHANGED', {test: 'test1'});
spyOn(scope, '$broadcast').and.callThrough();
expect(something).toEqual('something');

Case 2:

scope.$broadcast('TEST_CHANGED', {test: 'test2'});
spyOn(scope, '$broadcast').and.callThrough();
expect(something2).toEqual('something2');
Igy
  • 43,710
  • 8
  • 89
  • 115
KATHERINE
  • 109
  • 1
  • 8
0

As far as "toHavebeenCalled" concerned, there is no need of "andCallThrough()". Simple spy would work. In your case your arguments are different.

You are broadcasting like, rootScope.$broadcast('updatecrappy', [{id : 2, name : 'crappy'}]);

But you expect :

expect(rootScope.$broadcast).toHaveBeenCalledWith('updscenes', [{id : 2, name : 'crappy'}]);

look at argument " updarecrappy" but in tohavebeencalled it is "updscenes".

vipul patel
  • 668
  • 4
  • 7
0

Look at the sample code below, it works well for me.

Emitting sample event 'onTimeChanged' from $rootScope. My controller has a listener 'onTimeChanged' and I am calling a method timeChanged() inside it.

describe('onTimeChanged()', function() {
    it('should call another method or controller', function() {
        spyOn(searchCtrl, 'timeChanged');
        $rootScope.$emit('onTimeChanged');
        expect(searchCtrl.timeChanged).toHaveBeenCalled();
    });
});

Note that I did not spy on method '$emit' of rootScope.

pixlboy
  • 1,452
  • 13
  • 30