Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Karma/Jasmine Unit Testing an AngularJS Service with Dependencies

I'm a novice programmer who is very new to both AngularJS and the practice of unit testing. I've spent hours trying to find the solution to this but I'm becoming increasingly more confused. If anyone could point me in the right direction I would greatly appreciate it. I'll try to be as descriptive as possible.

The situation is this:

I have created a service in AngularJS (Service A) that has a couple of functions. Each of these functions makes an $http GET request to a REST API and returns an $http promise object that contains JSON data. Within these functions, the URL is constructed through the implementation of another very simple service (Service B) that has been injected as a dependency into Service A. I have created a mock of Service B to isolate it from all of its dependencies. Both of these services are defined inside of the same module named "services". In this case, there is no real need for this dependency but I just want to understand how it works.

Using Jasmine, I would like to construct a unit test for Service A to ensure that the requests it is making to the API are constructed correctly and possibly if the correct JSON data is being returned. At the same time, I do not want any real API calls to be made.

This is what I know:

$httpBackend mock is what I need to be able to make fake calls to the API and it provides functionality to expect certain requests and return specified results.

I need to test the real Service A and inject the mock I've created of Service B. I know there are ways to do this using Jasmine Spies and $provide. I've also seen examples where sinon.js is used, and I'm not sure which is the best approach.


I will post my source code below, which is written in CoffeeScript.

Service A:

'use strict'

angular.module("services")
  .service("ServiceA", ["$http", "ServiceB", ($http, ServiceB) ->

    #Uses underscore.js to set this default attribute
    defaults = withCredentials:true

    getVarset: (itemName, options={}) ->
      options.method = "GET"
      options.url = ServiceB.makeUrl("item/#{itemName}")

      $http _.defaults(options, defaults)

    getVarsets: (options = {}) ->
      options.method = "GET"
      options.url = ServiceB.makeUrl("items")

      $http _.defaults(options, defaults)

    getModelsForVarset: (itemName, options = {}) ->
      options.method = "GET"
      options.url = ServiceB.makeUrl("item/#{itemName}/prices")

      $http _.defaults(options, defaults)
  ])

Service B:

'use strict'

angular.module('services')
  .service 'ServiceB', [ ->

    # Just return the string
    # This service builds the real URL, but I've removed this
    makeUrl: (Url) -> 
      "#{Url}"
  ]
like image 586
The Zuidz Avatar asked Oct 03 '22 17:10

The Zuidz


1 Answers

so are you saying that you know how to do this with $provide/Jasmine spies and are looking for alternatives? I've mostly just used the $provide/spy method for mocking and it's worked out really well for me so far.

something like:

beforeEach(function() {

    // set up a default value for your mock
    bMock = {
        makeUrl: jasmine.createSpy('makeUrl() mock').andReturn('http://www....')
    }

    // use the $provide service to replace ServiceB
    // with your mock
    module('services', function($provide) {
        $provide.value('ServiceB', bMock);
    });

});

it('should do what its supposed to do', function() {
     // test...
});

then, if you want to use $httpBackend to mock the http requests in service A, you just need to use the $injector service to grab $httpBackend, then call .when(...) on it to set things up, a la http://docs.angularjs.org/api/ngMock.$httpBackend

like image 113
UnicodeSnowman Avatar answered Oct 18 '22 11:10

UnicodeSnowman