2014-08-27 7 views
6

Ho il seguente comando (accorgo che al momento esemplificazione faccio una chiamata esplicita a $scope.getNotifications()):

bla.controller("myctrl", [ 
    "$scope", "$http", "configs", function ($scope, $http, configs) { 

     $scope.getNotifications = function() { 
      $http.get("bla/blabla").success(function (data) { 

      }); 
     }; 

     $scope.removeNotification = function (notification) { 
      var index = $scope.allNotifications.indexOf(notification); 
      $scope.allNotifications.splice(index, 1); 
     }; 

     $scope.getNotifications(); 
    } 
]); 

poi faccio alcuni test di unità (si noti che il controllore viene istanziato nella prima di ciascuno):

describe("blaController", function() { 

    var scope, $httpBackend; 

    beforeEach(module('bla')); 

    beforeEach(inject(function ($controller, $rootScope, _$httpBackend_) { 
     scope = $rootScope.$new(); 
     $httpBackend = _$httpBackend_; 
     $controller('blaCtrl', { $scope: scope }); 
    })); 

afterEach(function(){ 
    //assert 
    $httpBackend.verifyNoOutstandingExpectation(); 
    $httpBackend.verifyNoOutstandingRequest(); 
}); 

it("should get all notifications from server when instantiated", function() { 
    //arrange 
    $httpBackend.expectGET("api/v1/notifications").respond(200, {}); 
    $httpBackend.flush(); 

    //act - done implicitly when controller is instantiated 
}); 

it("should store all notifications from server on the client when success call to server", function() { 
    //arrange 
    $httpBackend.whenGET("api/v1/notifications").respond(200, [{ a: 1, b: 2 }, { c: 3, d: 4 }]); 
    $httpBackend.flush(); 

    //act - done implicitly when controller is instantiated 

    //assert 
    expect(scope.allNotifications).toEqual([{ a: 1, b: 2 }, { c: 3, d: 4 }]); 
}); 

Tutto è andato bene fino ad ora. Tutti i test passano Ma quando aggiungo un nuovo test (vedi sotto) che non richiede alcuna chiamata HTTP fallisce perché nello afterEach() verifica per le aspettative ma non ci sono aspettative impostate nello removeNotification(). Questo è il messaggio di errore dal karma: PhantomJS 1.9.7 (Windows 8) notificationCenterController removeNotification dovrebbero rimuovere la data di notifica da th e lista FALLITA Errore: richiesta imprevisto: GET API/v1/notifiche Non più richiesta dovrebbe

it("should remove the given notification from the list", function() { 
      //arrange 
      var targetObj = { a: 2 }; 
      scope.allNotifications = [{ a: 1 }, targetObj, { a: 3 }]; 

      //act 
      scope.removeNotification(targetObj); 

      //assert 
      expect(scope.allNotifications).toEqual([{ a: 1 }, { a: 3 }]); 
     }); 

La maggior parte del mio test ha chiamate http, quindi è consigliabile inserire la verifica in afterEach. Mi chiedevo quale altra opzione dovrei evitare di copiare incollare il corpo afterEach nei test N-1. C'è un modo per dire a $httpBackend di ignorare eventuali chiamate?

+0

Nel nostro progetto abbiamo spostato le chiamate $ http a una fabbrica e deriso quella fabbrica nei test del controller. In questo modo è possibile testare la fabbrica utilizzando $ httpBackend e testare la logica del controller senza $ httpBackend. – knutesten

+0

Il controller ha '$ scope.getNotifications();' sulla sua ultima riga in modo che vengano effettuate chiamate http. Stai iniziando il controller in un 'beforeEach', quindi verrà ricreato per ogni singolo test e quindi eseguirà anche la chiamata a' $ http' in ogni singolo test. – ivarni

+0

Sì hai ragione Ho aggiornato la risposta per essere più chiara.So che sono stati fatti ma non per il metodo in questione che voglio testare, quindi non cerco aspettative nel test, ma le verifiche avvengono ancora da quando sono state fatte dopo ogni test. –

risposta

3

puoi completare il test descrivendo il blocco come di seguito.

describe("blaController", function() { 

    var scope, $httpBackend; 

    beforeEach(module('bla')); 

    beforeEach(inject(function ($controller, $rootScope, _$httpBackend_) { 
     scope = $rootScope.$new(); 
     $httpBackend = _$httpBackend_; 
     $controller('blaCtrl', { $scope: scope }); 
    })); 

    describe('test http calls', function() { 

     afterEach(function(){ 
      //assert 
      $httpBackend.verifyNoOutstandingExpectation(); 
      $httpBackend.verifyNoOutstandingRequest(); 
     }); 

     it("should get all notifications from server when instantiated", function() { 
      //arrange 
      $httpBackend.expectGET("api/v1/notifications").respond(200, {}); 
      $httpBackend.flush(); 

      //act - done implicitly when controller is instantiated 
     }); 

     it("should store all notifications from server on the client when success call to server", function() { 
     //arrange 
      $httpBackend.whenGET("api/v1/notifications").respond(200, [{ a: 1, b: 2 }, { c: 3, d: 4 }]); 
      $httpBackend.flush(); 

      //act - done implicitly when controller is instantiated 

      //assert 
      expect(scope.allNotifications).toEqual([{ a: 1, b: 2 }, { c: 3, d: 4 }]); 
     }); 

    }); 

    describe('other tests', function(){ 
     it("should remove the given notification from the list", function() { 
      //arrange 
      var targetObj = { a: 2 }; 
      scope.allNotifications = [{ a: 1 }, targetObj, { a: 3 }]; 

      //act 
      scope.removeNotification(targetObj); 

      //assert 
      expect(scope.allNotifications).toEqual([{ a: 1 }, { a: 3 }]); 
     }); 
    }); 
}); 
1

È possibile spiare $ http.get in una suite separata, che dovrebbe funzionare (pseudo-codice di seguito).

describe("backend", function() { // your code from before }); 

describe("non-backend", function() { 

    var scope, $http; 

    beforeEach(module('bla')); 

    beforeEach(inject(function ($controller, $rootScope, _$http_) { 
     scope = $rootScope.$new(); 
     $http = _$http_; 
     spyOn($http, 'get').and.callFake(function() { 
      return { some: 'data' }; 
     }); 
     $controller('blaCtrl', { $scope: scope, $http: $http }); 
    })); 

    it("should remove the given notification from the list", function() { 
     //arrange 
     var targetObj = { a: 2 }; 
     scope.allNotifications = [{ a: 1 }, targetObj, { a: 3 }]; 

     //act 
     scope.removeNotification(targetObj); 

     //assert 
     expect(scope.allNotifications).toEqual([{ a: 1 }, { a: 3 }]); 
    }); 
}); 
1

Dal momento che si stanno facendo la richiesta HTTP nel costruttore, e il costruttore viene eseguito ogni volta che si esegue un test, poi il httpBackend devono essere preparati a rispondere a questa richiesta ogni volta.

Quindi nel tuo beforeEach blocco si shoud essere impostando:

notificationsRequest = $httpBackend.expectGET("api/v1/notifications").respond(200, {}); 

Nei vostri casi di test, è possibile modificare questa risposta, chiamando di nuovo il metodo di rispondere, che sovrascrive la reazione impostata in questo modo:

it("should store all notifications from server on the client when success call to server", function() { 
    //arrange 
    notificationsRequest.respond(200, [{ a: 1, b: 2 }, { c: 3, d: 4 }]); 
    $httpBackend.flush(); 

    //act - done implicitly when controller is instantiated 

    //assert 
    expect(scope.allNotifications).toEqual([{ a: 1, b: 2 }, { c: 3, d: 4 }]); 
}); 

Ora, tutto ciò che devi fare è assicurarti di lavare le risposte dopo ogni test. Dato, questo potrebbe non essere conveniente, ma deve essere fatto se si desidera verifyNoOutstandingRequest poiché per definizione del costruttore, ci sarà una richiesta in sospeso. Ti suggerirei di farlo strutturando i test in suite nidificate e per quelli che non testano il recupero delle notifiche, svuotando la richiesta nel proprio blocco beforeEach.