Dan J Miller
Dan J Miller

Reputation: 118

Angular/Jasmine: spying on method passed to $rootScope.$on with and without anonymous function wrapper

In angular, I am calling a method when an event listened for by $rootScope.$on is fired.

$rootScope.$on('someEvent', some.method);

I am spying on the method in my jasmine tests, firing the event from my tests and then checking to see if the method was called.

spyOn(some, 'method');
$rootScope.$broadcast('someEvent');
expect(some.method).toHaveBeenCalled(); // fails

This test fails. Also, the method is actually called (verified through console logs).

Now, I can wrap the method in an anonymous function like so:

$rootScope.$on('someEvent', function () { some.method(); });

And the tests pass.

spyOn(some, 'method');
$rootScope.$broadcast('someEvent');
expect(some.method).toHaveBeenCalled(); // passes

In this case, the method itself is not actually called (as expected, because it is being spied on).

I have set up a suite of tests to fully illustrate in this Plunk: http://plnkr.co/edit/3S5J47OeCBsL0RASGe5P?p=preview

The method passed to $rootScope.$on is called when the event fires regardless of whether it is wrapped in an anonymous function. However the spy only registers when wrapped in an anonymous function.

In jasmine, is it possible to successfully spy on an angular function passed by reference to $rootScope.$on. If so, how? If not, why not?

Upvotes: 1

Views: 510

Answers (1)

psicopoo
psicopoo

Reputation: 1676

spyOn() is a mutator. I forked your Plunk and added some some console logs.

Consider the call to register a spy:

spyOn(test, 'bar').andCallThrough();

This creates a new spy function that does the spy work and then calls the spied function. The spy is then stored on the object in place of the original function. In your "without anonymous wrapper" example you register the event to call the function that is value of test.bar at the time the spy is registered:

$rootScope.$on('withoutFunctionWrapper', test.bar);

Whereas with the wrapper, the event will trigger whatever test.bar is set to at the time of the event, which in your case is the spy:

$rootScope.$on('withFunctionWrapper', function() {
  test.foo();
});

To illustrate:

enter image description here

So you could try calling spyOn() first, if the function you're spying on can be made available before $rootScope.$on() is called. Otherwise, you'll need the wrapper.

Upvotes: 2

Related Questions