6

I am trying to do an unit test a service in my case

In my test controller

myService.getItem('/api/toy/' + scope.id).success(
   function(toy) {
       $scope.toy = toys.details;
   }
);

MyService

angular.module('toyApp').service('myService', ['$http',
    function($http) {
        var service = {};
        return {
            getItem: function(url) {
                return $http.get(url);
            },
        };
    }
]);

Test file.

describe('toy ctrl', function () {
    var $httpBackend, ctrl, myService;

    beforeEach(module('toyApp'));

    beforeEach(inject(function (_$controller_, _$httpBackend_, _$rootScope_, __myService_) {
        scope = _$rootScope_.$new();
        $httpBackend = _$httpBackend_;
        myService = _myService_;

        ctrl = _$controller_('toyCtrl', {
            $scope: scope
        });      

    }));

    describe('call my service', function() {
        it('should make request when app loads', function() {
            $httpBackend.expectGET('/api/toy/123').respond({id:123, detail:456 });
            myService.getItem('/api/toy/123').then(function(toy){
                expect(scope.toy.detail).toBe(456);
            })
            $httpBackend.flush();   
     })
 })

I am getting

Error: Unexpected request: GET /api/toy/123
No more request expected

If I take out $httpBackend.flush(), the error is gone but it won't cover the

  function(toy) {
       $scope.toy = toys.details;
   }

part. I want to cover the function call and not sure how to do this. Can anyone help me about it? Thanks a lot

4
  • Are you unit testing controller or service (should make request when app loads seems like controller)? Commented May 18, 2015 at 17:52
  • I am testing service inside my controller Commented May 18, 2015 at 17:54
  • 1
    Ok. in that case you could just mock your service out and inject it to the controller. And test value against scope. Basically what i am trying to say is when you unit test controller you just have to test the controller logic myService.getItem('/api/toy/123') is irrelevant actually. Commented May 18, 2015 at 17:55
  • @PSL could you please provide an example? +1 Commented May 18, 2015 at 17:58

1 Answer 1

3

Seems like you are "unit" testing the controller, so you don't have to bring in the service in picture as you just need to test the controller logic. You could create a mock service and inject it while creating the controller in your test.

Example:

var mockItem = {details:{//somestuff}, id:'1'};// set up a mock object to test against later
//....
beforeEach(inject(function (_$controller_, _$httpBackend_, _$rootScope_, _$q_) {
    scope = _$rootScope_.$new();
    $httpBackend = _$httpBackend_;

    //Set up mock
    myService = jasmine.CreateSpyObj('myService', ['getItem']); 
    myService.getItem.and.returnValue($q.when(mockItem ));

    ctrl = _$controller_('toyCtrl', {
        $scope: scope,
        myService: myService //<-- Pass it here
    });      

}));


  //.....Assuming you are making the call when controller is instantiated
  it('should make request when app loads', function() {
     expect(myService.getItem).toHaveBeenCalled();
     //You could also check as below
     //expect(myService.getItem).toHaveBeenCalledWith(expectedidpassedin);
     scope.$digest(); //Resolve $q promise callback
     expect($scope.toy).toEqual(mockItem .details);
  });

If you are specifically unit testing your service alone you could do:

it('should make request when app loads', function() {
        var resp;
        $httpBackend.expectGET('/api/toy/123').respond({id:123, detail:456});
        myService.getItem('/api/toy/123').then(function(response){
            resp = response.data;
        });
        $httpBackend.flush();   
        expect(resp.detail).toEqual(456);
 });

In your controller instead of chaining success use then

 myService.getItem('/api/toy/' + scope.id).then(
   function(response) {
       $scope.toy = response.toys.details;
   });
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks I am getting error when use .then, it seems like it can't find the response if I use then. +1
@FlyingCat That should not be an issue. then is a part of q promise. success is part of http promise which is just a wrapper over q promise. Then should just be available everywhere. Also make sure you do $q.when(data) in your test. Also note that when you use then response.data is response that you get a first argument in success callback

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.