Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write unit test for http request?

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

like image 901
FlyingCat Avatar asked May 18 '15 17:05

FlyingCat


1 Answers

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;
   });
like image 107
PSL Avatar answered Oct 30 '22 21:10

PSL