How to Unit Test an Angular Application – Service with Requests to Backend

I noticed that a lot of developers write unit tests that is not really unit testing the code. Many dependencies are not mocked correctly or not mocked at all so unit test is not serving its true purpose.

Personally, I was struggling with how to do unit test quite a bit just like many others but now I think I have a solid concept on how to write unit test correctly.

There will be more posts coming up covering unit testing different part of your Angular application.

For now, let’s start with a simple service with backend calls.

angular.module('myApp').service('MyService', MyService);
MyService.$inject = ['$resource'];
function MyService($resource) {
  var me = this;
  me.myResource = $resource('/users/:id', {}, {});
}
MyService.prototype.getUsers = function() {
  var me = this;
  return me.myResource.query().$promise;
};
MyService.prototype.getUserById = function(userId) {
  var me = this;
  return me.myResource.get({id: userId}).$promise;
};

Consider the above service, what do we want to unit test for in this case?
Only thing we really care about is that the functions are making the correct HTTP requests to the server. As long as the URL is correct, we really don’t care for anything else on the unit test. Your service code has no control of what server will return to you. Now let’s see how the unit test would look like.

describe('my service', function () {
  var MyService, httpBackend;

  beforeEach(module('myApp'));

  beforeEach(inject(function (_MyService_, $httpBackend) {
    MyService = _MyService_;
    httpBackend = $httpBackend;
  }));

  it('should match URL when get users', function () {
    httpBackend.expectGET('/users');
    MyService.getUsers();
  };

  it('should match URL when get user by ID', function() {
    httpBackend.expectGET('/users/1');
    MyService.getUserById(1);
  };
});

As far as unit test this service goes, this should be all you need. You don’t care about the data that is returned to you because provided the input is correct (the endpoint URL), the result testing should be covered in the unit test for the server side.

  • ngDeveloper

    I’ll disagree, in that you’re not testing enough. The service should test the transformation that happens after the method is called. ie. if you POST username/password and expect a response token, the transformation that happens is that the user 1) was LoggedOut, 2) becomes LoggedIn. So your test would include: 1) pre-test expect `isAuthenticated()` to be false, 2) call `getToken()`, 3) post-test expect `isAuthenticated()` to be true. In this case, success case would be that there is now a token stored in local storage.

    • Jack Yen

      Thanks for your feedback! Definitely agree with the points you mentioned. Now, consider the example that the specific service that they’re returning promises, so typical transformation in this case is done in the controller.

      For example,

      MyService.getUsers().then(function(data) {
      //Transform data
      }, function(data) {
      //Error handling
      });

      In this case, do you think the unit test is sufficient since the transformation is done in the controller which would be tested inside the controller?

      I’ll make another post regarding the way I approach controller testing when I get a chance!

  • Larry Zhao

    What kind of output do you get when you run this?

    • Jack Yen

      just test results success or failure

  • Pingback: Unit Test a Controller in Angularjs | Jack Yen()