How to inject a service in a directive unit test in AngularJS

14,398

To decorate, stub, provide mocks or override any given service, you may use the $provide service. $provide.value, $provide.decorator etc. Documentation here.

Then you can do stuff like this:

 var $location;

 beforeEach(function() {
    module('studentportalenApp', function($provide) {
      $provide.decorator('$location', function($delegate) {

        $delegate.path = jasmine.createSpy();

        return $delegate;
      });
    });

    inject(function(_$location_) {
      $location = _$location_;
    });

  });

...

it('should visit the link in scope.redirectUrl when enter is pressed', inject(function ($rootScope, $compile, $location) {
    element = angular.element('<input type="text" goto-on-enter>');
    element = $compile(element)($rootScope);

    $rootScope.redirectUrl = 'http://www.google.com';
    $rootScope.$digest();

    var e = jQuery.Event('keypress');
    e.keyCode = 13;
    element.trigger(e);

    $rootScope.$digest();

    expect($location.path).toHaveBeenCalledWith('http://www.google.com');

}));
Share:
14,398

Related videos on Youtube

Kenneth Lynne
Author by

Kenneth Lynne

Systems Consultant and web-fanatic from Norway

Updated on June 14, 2022

Comments

  • Kenneth Lynne
    Kenneth Lynne about 2 years

    I need to test a directive that does some calls to some injected services. The following piece of code is an example directive, that listens for events, and redirects the browser if enter is pressed inside a specified element.

    Edit: I get the feeling I may be wading in E2E testing land?

    angular.module('fooApp')
      .directive('gotoOnEnter', ['$location', function ($location) {
    
        var _linkFn = function link(scope, element, attrs) {
    
            element.off('keypress').on('keypress', function(e) {
                      if(e.keyCode === 13)
                      {
                           $location.path(scope.redirectUrl);
                      }
                  });
        }
    
        return {
          restrict: 'A',
          link: _linkFn
        };
      }]);
    

    The problem is that I have not figured out how to inject services to spy on them in directives.

    My proposed solution looks like this: It does not work, as expected, because I have not managed to inject a $locacion service successfully to spy on.

    describe('Directive: gotoOnEnter', function () {
      beforeEach(module('fooApp'));
    
      var element;
    
      it('should visit the link in scope.url when enter is pressed', inject(function ($rootScope, $compile, $location) {
    
        element = angular.element('<input type="text" goto-on-enter>');
        element = $compile(element)($rootScope);
    
        $rootScope.redirectUrl = 'http://www.google.com';
        $rootScope.$digest();
    
        var e = jQuery.Event('keypress');
        e.keyCode = 13;
        element.trigger(e);
    
        spyOn($location, 'path');
    
        expect($location.path).toHaveBeenCalledWith('http://www.google.com');
      }));
    

    This yields

    Expected spy path to have been called with [ 'http://www.google.com' ] but it was never called.
    
    • Jedidiah Hurt
      Jedidiah Hurt over 10 years
      You need to create the spy before attempting to navigate. I believe that if you move the call to 'spyOn' to the top of the function, it will work as intended.
  • primavera133
    primavera133 about 10 years
    url to documentation has changed: docs.angularjs.org/api/auto/object/$provide