10
votes

I have a function like this:

 $scope.doIt = function( x, y )
 {
   $timeout( function ()
   {
     $rootScope.$broadcast( 'xxx',
     {
        message: xxx,
        status: xxx
     } );
   } ); 
 }

This function works fine so far. But while writing a test I had some trouble.

describe( 'test doIt function...', function ()
      {
        var $rootScope, $timeout;

        beforeEach( inject( function ( _$rootScope_, _$timeout_ )
        {
          $rootScope = _$rootScope_;
          $timeout = _$timeout_;
          spyOn( $rootScope, '$broadcast' );
          spyOn( scope, 'doIt' ).and.callThrough();
        } ) );

        it( 'test broadcast will be called', inject( function ()
        {
          var testObj = {
            message: 'test1',
            status: 'test2'
          };

          scope.doIt( 'test1', 'test2' );

          expect( $rootScope.$broadcast ).not.toHaveBeenCalledWith( 'xxx', testObj );

          $timeout.flush();

          expect( $rootScope.$broadcast ).toHaveBeenCalledWith( 'xxx', testObj );

        } ) );
      }
    );

This will end up in the following error:

TypeError: 'undefined' is not an object (evaluating '$rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, $location.$$state, oldState).defaultPrevented')

Why? What I am doing wrong? Without $timeout in function and test it works fine.

Thanks in advance for your help.

:)

Edit: Another broadcast then expected is issuing this issue. hmm

2
which line is the error pointing to? the one before flush or after? - hjl
This seems a bit contrived, so I'm not sure what you're really trying to get at. I assume you're defining "scope" somewhere else in the spec (and $scope in the code-under-test.) Are you injecting $rootScope into the code-under-test anywhere? Regardless, this seems like a less-than-ideal way to test: rather than spying on $rootScope.$broadcast, why not rather simply listen for the broadcast in your spec the same way you would in normal code (i.e., $scope.$on) - David Pisoni

2 Answers

8
votes

I have fixed this problem by returning preventDefault

spyOn($rootScope, '$broadcast').and.returnValue({preventDefault: true})
1
votes

Problem -

Angular framework is trying to invoke $broadcast function and expecting a object from that function in return which have defaultPrevented property.

But because of

spyOn( $rootScope, '$broadcast' );

statement in beforeEach block the actual implementation of $broadcast couldn't be invoked and hence it does not return a object which contains defaultPrevented property.

Solution -

Move the spyOn( $rootScope, '$broadcast' ); statement from beforeEach block to it block exactly before scope.doIt( 'test1', 'test2' );.