Monday 28 January 2013

Angularjs multiple services and Jasminejs unit testing a beginners guide

Angularjs using multiple services and Unit testing services with jasminejs 

(update: 29/01/12) I have moved away from angular.module('ServiceName', value( 
and instead use myServices.factory('ServiceName', function(){ as I found  it did not do as I expected to.

Services are a bit harder to unit test and get configured than controllers.
Most examples have a single service or 2 services in a single file.
This is harder to maintain with source control as multiple people could be making changes on the same file.  Here is how to set up and use services in different files.

I have also included some example code on how to Unit test your angularjs service with jasperjs.

/app/app.js

Add a new module myApp.services do your app.js and then reference it inside your angular.module [] brackets.  The deceleration of the 'myApp.services' allows you then to use this module in the services. 

var myServices = angular.module('myApp.services', []);
var myApp = angular.module('myApp', ['myApp.services'])
  .config(['$routeProvider', function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/my-controller.html',
        controller: 'MyCtrl'
      });
  }]); 

/app/index.html

Add 2 services to your index.html so they are included in your app.
 
<script src="scripts/services/first-service.js" ></script> 
<script src="scripts/services/second-service.js" ></script> 

/app/services/first-service.js

Here is a example service containing one value and one function.

'use strict';

myServices.factory('FirstService', function(){
  var firstService = {};
  firstService.aValue = 'one';
  firstService.next = function (param1) {
    return this.aValue+ ' two';
  };
  return firstService;
}); 

/app/services/second-service.js

Second service does nothing special just a value an a method also.

myServices.factory('SecondService', function(){
  var sercondService = {};
  secondService.animal = 'penguin';
  secondService.whereDoTheyLive = function () {
    return this.aValue+ ' live in the Antarctic NOT in the Arctic.  The Arctic is for the Polar Bears.';
  };
  return secondService;
}); 

How to do it wrong

/app/services/first-service.js  
angular.module('myApp.services',[]).value('FirstService',
...
); 

/app/services/second-service.js

angular.module('myApp.services',[]).value('SecondService',
...
);
 

The problem with the above

The problem is the first-service is the first called by the inclusion in the index.html first.

You cannot add 2 or more services onto the myApp.services where both use the [] notation.  
I found it is best to assign the myApp.services in the /app/app.js and then just reference it in all the other services you are going to create.

Testing using Jasperjs

CoffeeScript

The below code will setup the first service for testing and importantly the inject will use the global service var as the service so we can run the methods and values upon it.

describe 'FirstService', ->
  service = {}
  beforeEach module('clientApp.services')

  beforeEach inject (FirstService) ->
    service = FirstService

  describe 'the first service', ->
    it 'should have a value',  ->
      expect(service.aValue).toEqual('one')

Javascript

The generated javascript that will be run by the jasperjs.

// Generated by CoffeeScript 1.4.0
(function() {
  describe('FirstService', function() {
    var service;
    service = {};
    beforeEach(module('myApp.services'));
 
    beforeEach(inject(function(FirstService) {
      return service = FirstService;
    }));
    return describe('the first service', function() {
      it('should have a value', function() {
        expect(service.aValue).toEqual('one');
      });
    });
  });

}).call(this);

No comments:

Post a Comment