Complexity
Complexity

Reputation: 5830

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

Upvotes: 0

Views: 1282

Answers (2)

user1364910
user1364910

Reputation:

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.

Upvotes: 1

Italo Ayres
Italo Ayres

Reputation: 2663

I had this doubt a while ago. To inject your controllers and services into your tests you need to use a tool called Angular Mocks. Here's some official reference about it.

I sugest you use it together with the Karma enviroment.

Here's a great Getting Started tutorial:

https://www.airpair.com/angularjs/posts/unit-testing-angularjs-applications

This other one is for the Ionic Framework, but can still aplly to your case

Hope it can help.

Upvotes: 0

Related Questions