0

I have a very basic factory service that I would like to unit test. I have tried $rootScope.$digest() and $rootScope.$apply() before the expect statements and neither the success or failure handlers are being called at all in the unit test - they are called fine in the app in the context of the controller that's using this factory, though.

example.service.js:

(function (angular) {
  'use strict';

  angular
    .module('exampleModule')
    .factory('ExampleApi', ExampleApi);

  ExampleApi.$inject = ['$http'];
  function ExampleApi($http) {
    return {
      get: getExampleData
    }

    function getExampleData() {
      return $http.get('example-endpoint');
    }
  }

})(angular);

example.service.spec.js

'use strict';

describe('Example API service', function() {
  var $httpBackend;
  var $rootScope;
  var ExampleApi;

  beforeEach(module('exampleModule'));

  beforeEach(inject(
    function(_$httpBackend_, _$rootScope_, _ExampleApi_) {
      $httpBackend = _$httpBackend_;
      $rootScope = _$rootScope_;
      ExampleApi = _ExampleApi_;
  }));

  it('calls the success handler on success', function() {
    $httpBackend
      .expectGET('example-endpoint')
      .respond(200);

    var handlerStubs = {
      success: function() {
        console.log("success called");
      },
      failure: function() {
        console.log("failure called");
      }
    }

    spyOn(handlerStubs, 'success').and.callThrough();
    spyOn(handlerStubs, 'failure').and.callThrough();
    ExampleApi.get().then(handlerStubs.success, handlerStubs.failure);
    //$rootScope.$digest();
    $rootScope.$apply();
    expect(handlerStubs.failure.calls.count()).toEqual(0);
    expect(handlerStubs.success.calls.count()).toEqual(1);
  });
});
anjunatl
  • 1,027
  • 2
  • 11
  • 24
  • 3
    after `response(200);` try `$httpBackend.flush();` – Luke Hutton Feb 08 '17 at 19:16
  • @LukeHutton It didn't work right after `response(200)`, but it DID after the `ExampleApi.get()` statement. If you'll write that as an answer, I'll mark it as the correct one. Thank you very much! (After the `response` line, it said `Error: No pending request to flush ! ` which is why I thought to try it after the `get()`.) – anjunatl Feb 08 '17 at 19:19

1 Answers1

1

$httpBackend mock contains a flush() method, which when called, will resolve the get request giving you synchronous control of the code under test. When $httpBackend.flush(); is called, the expectation .expectGET is verified.

From the Angular Docs:

The $httpBackend used in production always responds to requests asynchronously. If we preserved this behavior in unit testing, we'd have to create async unit tests, which are hard to write, to follow and to maintain. But neither can the testing mock respond synchronously; that would change the execution of the code under test. For this reason, the mock $httpBackend has a flush() method, which allows the test to explicitly flush pending requests. This preserves the async api of the backend, while allowing the test to execute synchronously.

Luke Hutton
  • 10,612
  • 6
  • 33
  • 58