1

I am trying to test my controller using jasmine. Basically, when the controller is created it will call a service to make http request. I am using httpBackend to get the fake data. When I try to run the test I always get the error "No pending request to flush". If I remove the httpBackend.flush() then the test fails because controller.data.name is undefined. Can anyone know why it happens like that? Thanks.

The code for the module is here:

var myModule = angular.module('myModule', ['ngMockE2E']);
myModule.run(function($httpBackend){
  $httpBackend.whenGET('/Person?content=Manager').respond(function (){
     var response = {'name':'Bob','age':'43'}
     return [200,response];
  })
});

The code for the service:

myModule.factory('myService',function($http){
     return {
        getData: function(position){
             return  $http.get('/Person?content='+position); 
        }
     }
});

The code for controller is:

myModule.controller('myController',function(xrefService){
    var _this = this;
    _this.data ={};
    _this.getData = function(position){
        myService.getData(position).then(function(response){
            _this.data = response.data
        });
    }
    _this.getData("Manager");
})

The code to test the controller is:

describe("Test Controller",function(){
   var controller,httpBackend,createController;
   beforeEach(module('myModule'));
   beforeEach(inject(function($controller,$httpBackend){      
      createController = function(){
         return $controller('myController');
      }
      httpBackend = $httpBackend;     
   }));
   it("should return data",function(){
      controller = createController();
      httpBackend.flush();
      expect(controller.data.name).toEqual("Bob");
   });      
})
Steven
  • 415
  • 1
  • 4
  • 12

2 Answers2

1

The angular documentation says the following about $httpbackend for ngMockE2E:

Additionally, we don't want to manually have to flush mocked out requests like we do during unit testing. For this reason the e2e $httpBackend flushes mocked out requests automatically, closely simulating the behavior of the XMLHttpRequest object.

So, short answer: it doesn't exist and you don't need it.

ngstschr
  • 2,119
  • 2
  • 22
  • 38
0

you are using $httpBackend.whenGET inside "The code for the module"

you should be using $httpBackend inside the test code as follows ...

it("should return data",function(){
  $httpBackend.expectGET('/Person?content=Manager').respond(function (){
      var response = {'name':'Bob','age':'43'}
      return [200,response];
  })
  controller = createController();
  httpBackend.flush();
  expect(controller.data.name).toEqual("Bob");
});      

also i would advise using expectGET instead of whenGET.

With whenGET you are saying if the request is made then response like so.

With expectGET you are saying ... a request will be made, when it is made respond like so, if the request is not made then fail the test.

PS if you put some console.log statements inside your controller code then you should see these log statements when you run your test suite. If not then you know your controller code is not even being hit.

also use ..

afterEach(function () {
      httpBackend.verifyNoOutstandingExpectation();
      httpBackend.verifyNoOutstandingRequest();
});

which will force test failure if expectations were not met.

danday74
  • 52,471
  • 49
  • 232
  • 283
  • 1
    Thanks danday74, it works now. However I wonder is there anyway if we can avoid using expectGET().respond() in the test method. Since that means I need to read the service code to test the controller. For example maybe I just want to test if controller.data is not empty (which means it receives some data from the backend). – Steven Apr 07 '16 at 03:12
  • now your'e thinking :) yeah you want to use a Jasmine spy. http://ng-learn.org/2014/08/Testing_Promises_with_Jasmine_Provide_Spy. – danday74 Apr 07 '16 at 03:44
  • spyOn(myService, 'getData').and.callFake(function() { var def = $q.defer(); def.resolve(YOUR_MOCK_HTTP_RESPONSE); return def.promise; }); expect(myService.getData).toHaveBeenCalled(); – danday74 Apr 07 '16 at 03:44
  • the code is saying ... when myService.getData() is called then respond with a fake response. The fake response returns a promise, exactly like your real method returns a promise. Then you can spy on your service function to see how many times it was called, what params it was called with, etc. you can continue to check the value in controller.data no probs - that will now be populated by the fake data you provide in your fak response. God bless :) Forgot to mention, your real HTTP won't now be called and so you do not need to expectGET anymore. – danday74 Apr 07 '16 at 03:48
  • 1
    Thanks danday74, it really helps. :D – Steven Apr 08 '16 at 02:58