FF5Ninja
FF5Ninja

Reputation: 525

How to spyOn a service to test it -- AngularJS/Jasmine

Have tried everything I've found on the internet to make this work with no success. Trying to test a function in my service, but according to my coverage I'm never accessing it. Any help would be greatly appreciated :)

Service:

'use strict';

angular.module('Service').service('configService', function(
  $rootScope, $http) {
  var configObj = null;
  return {

    getConfig: function() {
      if (configObj != null) {
        console.log("returning cached config");
        return configObj;
      }
      else {
        return $http.get('conf.json').then(function(res) {
          $http.get(res.confLocation).then(function(
            locationResponse) {
            configObj = locationResponse;
            $rootScope.configObj = configObj;
            console.log($rootScope.configObj);
            return configObj;
          });
        });
      }
    }
  };
});

getConfig is never being accessed in the tests I've tried.

ServiceTests:

'use strict';
describe('Service: configService', function() {

  // load the controller's module
  beforeEach(module('Service'));

  var configService, $httpBackend, results, tstLocation, tstRes;
  var tstConfig = {
    "confLocation": "local-dev-conf.json"
  };

  var tstConfigEmpty = {};
  var tstConfigObjEmpty = {};

  var tstConfigObj = {
    "AWS": {
      "region": "us-east-1",
      "endpoint": "http://localhost:8133"
    }
  };

  // Initialize the controller and a mock scope
  beforeEach(inject(function(_configService_, _$httpBackend_) {
    inject(function($rootScope) {
      $rootScope.USERNAME = 'TESTER';
      $rootScope.configObj = tstConfigObj;
      $rootScope.locationResponse = tstLocation;
      $rootScope.res = tstRes;
    });

    configService = _configService_;
    $httpBackend = _$httpBackend_;

    //Problem here??
    spyOn(configService, 'getConfig').and.callFake(function() {
      return {
        then: function() {
          return "something";
        }
      };
    });

  }));
  it('should return a promise', function() {
    expect(configService.getConfig().then).toBeDefined();
  });

  it('should test backend stuff', inject(function() {

    results = configService.getConfig(tstConfig);
    $httpBackend.expectGET('conf.json').respond(tstConfig);
    $httpBackend.expectGET('local-dev-conf.json').respond(tstConfigObj);
    $httpBackend.flush();
  }));

  //Thanks Miles
  it('should check if it was called', inject(function() {
    results = configService.getConfig().then();
    expect(configService.getConfig).toHaveBeenCalled();

    });
    // console.log(results);
  }));

  it('should check for a null configObj', inject(function() {
    results = configService.getConfig(tstConfigObjEmpty).then(function() {
      expect(results).toBe(null);
    });
    // console.log(results);
    // console.log(tstConfigObj);
  }));

  it('should check for a non-null configObj', inject(function() {
    results = configService.getConfig(tstConfigObj).then(function() {

      // Any string is accepted right now -- Why??
      expect(results).toEqual("returning cached config");
      expect(results).toBe("returning cached config");
      expect(results).toBe("your mom"); // SHOULDN'T BE WORKING BUT DOES
      expect(results).toEqual("Object{AWS: Object{region: 'us-east-1', endpoint: 'http://localhost:8133'}}");
      expect(results).toBe("Object{AWS: Object{region: 'us-east-1', endpoint: 'http://localhost:8133'}}");
    });
    // console.log(results);
    // console.log(tstConfigObj);
  }));

  it('should check for null file', inject(function() {
    results = configService.getConfig(tstConfigEmpty).then(function() {
      expect(results).toEqual(null);
      expect(results).toBe(null);
    });
  }));

  it('should test a valid file', inject(function() {
    results = configService.getConfig(tstConfig).then(function() {
      expect(results).not.toBe(null);
      expect(results).toEqual("Object{confLocation: 'local-dev-conf.json'}");
    })
});

I think I'm using spyOn wrong, or not accessing getConfig in my tests properly. Thoughts?

EDIT: Here is my code coverage

EDIT 2: Changed test 3 thanks to a problem found by Miles, still no update on test coverage though. Something is wrong with my spyOn logic as Amy pointed out. I shouldn't be using callFake it seems?

EDIT 3: Got it accessing the function now thanks to Miles. Had to change my spyOn to:

spyOn(configService, 'getConfig').and.callThrough(); 

then add the test case:

results = configService.getConfig(tstConfig).then();   
expect(configService.getConfig).toHaveBeenCalled();

Coverage now (still needs work)

Upvotes: 2

Views: 3975

Answers (3)

Miles P
Miles P

Reputation: 710

You have an issue here:

results = configService.getConfig(tstConfigObj).then(function() {
  expect(results).toHaveBeenCalled();
  expect(results).toHaveBeenCalledWith(tstConfigObj);
});

getConfig takes no parameters, and neither does then. Omitting these errors, results is assigned the string "something" from then. Even if the expect statements fire, you seem to be testing if a string has been called. Try this instead:

results = configService.getConfig().then();
expect(configService.getConfig).toHaveBeenCalled();

Upvotes: 0

Amy Blankenship
Amy Blankenship

Reputation: 6961

You're calling a fake instead of the function. So the logic inside of the function does not get called.

Upvotes: 1

Scott Fanetti
Scott Fanetti

Reputation: 136

What version of Jasmine are you using? The and.callFake syntax was added in Jasmine 2.0. Maybe the test suite just needs to point to the new version.

Jasmine 1.3 Docs

Jasmine 2.0 Docs

Upvotes: 0

Related Questions