Search code examples
javascriptangularjsunit-testingjasmineangularjs-factory

How to unit test an AngularJS service / factory


I'm already some time in the development using AngularJS, and what I do write works, but know I've come to a point where I would like to run unit tests on my AngularJS code.

I have created a very simple service that will inject a stylesheet onto the page, see the example code below:

var OfficeSuiteStylesheetInjectorModule = angular.module('OfficeSuiteStylesheetInjectorModule', []);

OfficeSuiteStylesheetInjectorModule.factory('stylesheetInjectorService', ['$q', function($q) {
    // Returns the service itself.
    return {
        inject: function(id, uri) {
            var deferred = $q.defer();

            // Embed the stylesheet into the page, but only when it's non-existing.
            if (!angular.element('#' + id).length) {
                    var link = StylesheetFactory.Create(id, uri);
                    {
                        link.onload = deferred.resolve;
                        angular.element('head').append(link);
                    }

                    return deferred.promise;
                }
        }
    }
}]);

It's not a big service, it's just dependend on $q for promises so that I can run additional logic when the stylesheet has been embedded in the page.

Now, I'm using Jasmine (I'm quite new to this) for testing my JavaScript code and I would like to test this module.

I have a skeleton:

// Tests for the angular 'StylesheetInjectorService'.
describe('StylesheetInjectorService', function() {
    var stylesheetInjectorService = {};

    // This code is executed before every test.
    beforeEach(function() {
        // Tells jamine that the module we're working on is the 'OfficeSuiteStylesheetInjectorModule'.
        angular.module('OfficeSuiteStylesheetInjectorModule');
    });

    // Ensures that it injects a stylesheet element into the page.
    it('Should inject a stylesheet element into the page.', function() {
        // How to test here that the stylesheet is injected?
    });
  });
});

How can I inject te service in the page and ensures that the stylesheet is loaded?

Edit: Loading service now works:

beforeEach(module('OfficeSuiteStylesheetInjectorModule'));

// This code is executed before every test.
beforeEach(function() {

    // Inject the required dependencies into the page.
    inject(function($injector) {
        stylesheetInjectorService = $injector.get('stylesheetInjectorService');
    });
});

The same question is still open however. How to test if a stylesheet was embedded in the page?

Any help is highly appreciated.

Kind regards


Solution

  • To write a spec for the attachment of a stylesheet to angular.element('head') I would change the logic a bit to attach it to $document.head.

    If you dont want to do that, I would recommend that you change your service into a directive seeing as how injecting a script element, is manipulating the DOM. That way you would kill two birds with one stone, as you would need to inject $compile to test your directive (which would enable you to $compile a custom head element to boot). But this is slightly "over the top" for now.

    Implementation:

    if (!angular.element('#' + id).length) {
      var link = StylesheetFactory.Create(id, uri);
    
        link.onload = deferred.resolve;
    
        $document.head.append(link);
    
        return deferred.promise;
      }
    }
    

    beforeEach:

    /**
     * Sorry, this was previously $location which was just 
     * such a silly mistake.
     */
    var $timeout; 
    
    beforeEach(function () {
      inject(function ($injector) {
        $timeout = $injector.get('$timeout');
      });
    });
    

    it:

    it('attaches the stylesheet to $document.head', function () {
      styleSheetInjectorService.inject('someId', '/path/to/some/stylesheet');
      $timeout.flush(); // flush promises
      expect(angular.element($document.head).lastChild[0].nodeName).to.eq('LINK');
    });
    

    Something along those lines should get you up and running. Bare in mind that the spec I wrote uses the chai#expect style assertions, and the mocha test framework. Edit the syntax to fit Jasmine if you mean to copy-paste.