9

I have a simple angularjs filter (it takes an id and converts it to a name string), that depends on a custom service to do its work:

angular.module('app').filter('idToName',
  function(User) {
    return function(id) {
      var result, user;
      result = '';
      if (id) {
        result = 'no name found';
        user = User.getById(id);
        if (user) {
          result = user.firstName;
        }
      }
      return result;
    };
  }
);

and I want to write a unit test for it. I would like to be able to inject a mock of the User service into the test.

I can do this for a controller unit test as shown in the documentation:

var mockUserService;

mockUserService = {
  getById: function(id) {
    return {
      firstName: 'Bob'
    };
  }
};

beforeEach(inject(function($rootScope, $controller) {
  var ctrl, scope, userService;
  userService = mockUserService;
  scope = $rootScope.$new();
  return ctrl = $controller('someController', {
    $scope: scope,
    User: userService
  });
}));

but replacing $controller with $filter in the beforeEach does not work, as I presume filters are constructed differently by angular (i.e. don't allow you to inject locals as a second parameter to the constructor.)

Has anyone come across this / solved this before?

CharlesB
  • 86,532
  • 28
  • 194
  • 218
David Bochenski
  • 309
  • 4
  • 11

1 Answers1

13

Ok, figured this out thanks largely to this answer.

The trick was to simply override the factory provider of the service, by using angular-mocks.js model function in a before each (angular just takes the last defined factory it would seem)

beforeEach(module(function($provide) {
  $provide.factory('User', function() {
    var getSync;
    getById = function(id) {
      return {
        firstName: 'Bob'
      };
    };
    return {
      getById: getById
    };
  });
}));

I suspect that I'll need to be careful with teardown between tests, but the injection into the filter now works fine.

Community
  • 1
  • 1
David Bochenski
  • 309
  • 4
  • 11