dman
dman

Reputation: 11064

Angular Unit Test with $rootScope.$on("name")

With Mocha and Jasmine, I am trying to unit test this service:

angular.module("main.loadbalancer").run(function($rootScope, $state, DeviceVal) {
  $rootScope.$on("$stateChangeStart", function(event, toState) {
    var checkUrlDirectEdit, device;
    device = window.location.hash.split("/")[2];
    checkUrlDirectEdit = function() {
      return DeviceVal.previousDevice !== device && toState.name !== "main" && DeviceVal.lb.id === "undefined";
    };
    if (typeof DeviceVal.lb.id === "undefined" && toState.name !== "main" || checkUrlDirectEdit()) {
      event.preventDefault();
      DeviceVal.previousDevice = device;
      $state.transitionTo("main", {}, {
        location: false
      });
    }
    if (DeviceVal.readonly && toState.name !== "main.loadbalancer.readonly") {
      event.preventDefault();
      $state.transitionTo("main.loadbalancer.readonly", {
        id: DeviceVal.lb.id
      }, {
        location: true,
        reload: true
      });
    }
    return window.addEventListener("hashchange", function() {
      return location.reload();
    });
  });
});

With this test:

describe("health check service", function() {
  var HealthCheckSvc;
  HealthCheckSvc = null;

  beforeEach(function() {
    return module("main.loadbalancer");
  });

  beforeEach(inject(function($rootScope, _$state_, _DeviceVal_) {
    this.$rootScope = $rootScope;
    $state = _$state_;
    $DeviceVal = _DeviceVal_;

    spyOn($state, "transitionTo")
    spyOn(this.$rootScope, "$on")


  }));

  it("do some junk", function() {
      toState = 'main.loadbalancer.nodes';
      this.$rootScope.$broadcast("$stateChangeStart", "event", "toState");
      expect(this.$rootScope.$on).toHaveBeenCalledWith("$stateChangeStart");

    });
});

Right now, I am having a hard time makeing making a expect for $rootScope.$on("$stateChangeStart"). I thought spyOn(this.$rootScope, "$on") and expect(this.$rootScope.$on).toHaveBeenCalledWith("$stateChangeStart"); would do it, but I get:

 Expected spy $on to have been called with [ '$stateChangeStart' ] but it was never called.
        Error: Expected spy $on to have been called with [ '$stateChangeStart' ] but it was never called.

Upvotes: 0

Views: 2165

Answers (2)

Michal Charemza
Michal Charemza

Reputation: 27012

It is possible, but I suspect the spy is being added after the run block is being run. A way around this, is to provide your own mock $rootScope object, with a stub/spied $on function, to the dependency injection system, which you can do before run has run.

describe('run block', function() {
  var $rootScope = null;

  beforeEach(module('plunker'));

  beforeEach(module(function($provide) {
    $provide.value('$rootScope', {
      '$on': jasmine.createSpy('$on')
    });
  }));

  beforeEach(inject(function(_$rootScope_) {
    $rootScope = _$rootScope_;
  }));

  it('should call $on with $stateChangeStart and a function', function() {
    expect($rootScope.$on).toHaveBeenCalledWith('$stateChangeStart', jasmine.any(Function));
  });
});

This can be seen working at http://plnkr.co/edit/XovukFUxOY479muQu4WW


Sidebar: I'm not sure whether you need this test. I think it's more important to make sure it does what it should do when the event fires.

Upvotes: 1

Khanh TO
Khanh TO

Reputation: 48972

The $rootScope.$on("$stateChangeStart" is called early and just to register an event listener.

When you call this.$rootScope.$broadcast("$stateChangeStart", "event", "toState");, $rootScope.$on is not called, but the code inside the handler (function(event, toState) {)

Upvotes: 2

Related Questions