2016-03-28 5 views
1

Nach einer Woche auf der Suche nach einer guten Antwort/Probe, habe ich mich entschieden, meine Frage zu posten.Der beste Weg Jasmine zu benutzen, um Angular Controller Calls zu Services mit Promise zu testen

Ich muss wissen, wie ist der beste Weg, und Test etwas zu kodieren:

-Controller

// my.controller.js 
(function() { 

    'use strict'; 

    angular.module('myApp.myModule').controller('Awesome', Awesome); 

    function Awesome($http, $state, AwesomeService) { 

    var vm = this; // using 'controllerAs' style 

    vm.init = init; 
    vm.awesomeThingToDo = awesomeThingToDo; 

    vm.init(); 

    function awesomeThingToDo() { 
     AwesomeService.awesomeThingToDo().then(function (data) { 
     vm.awesomeMessage = data.awesomeMessage; 
     }); 
    } 

    function init() { 
     vm.awesomeThingToDo(); // Should be ready on page start 
    } 
    } 
})(); 

Dienst

// my.service.js 
(function() { 
    'use strict'; 

    angular.module('myApp.myModule').factory('AwesomeService', AwesomeService); 

    function AwesomeService($resource, $http) { 

    var service = { 
     awesomeThingToDo: awesomeThingToDo 
    } 

    return service; 

    function awesomeThingToDo() { 

     var promise = $http.get("/my-backend/api/awesome").then(function (response) { 
      return response.data; 
     }); 

     return promise; 
    } 
    } 
})(); 

Meine App mit dieser Struktur OK funktioniert. Und meine Service Unit Tests sind auch in Ordnung. Aber ich weiß nicht, wie Unit-Tests auf Controller zu tun.

Ich habe versucht, so etwas wie dieses:

Specs

// my.controller.spec.js 
(function() { 
    'use strict'; 

    describe("Awesome Controller Tests", function() { 

    beforeEach(module('myApp.myModule')); 

    var vm, awesomeServiceMock; 

    beforeEach(function() { 
     awesomeServiceMock = { Is this a good (or the best) way to mock the service? 
     awesomeThingToDo: function() { 
      return { 
      then: function() {} 
      } 
     } 
     }; 
    }); 

    beforeEach(inject(function ($controller) { 
     vm = $controller('Awesome', {AwesomeService : awesomeServiceMock}); 
    })); 

    it("Should return an awesome message", function() { 
     // I don't know another way do to it... :(
     spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function() { 
     return { 
      then: function() { 
      vm.awesomeMessage = 'It is awesome!'; // <-- I think I shouldn't do this. 
      } 
     } 
     }); 

     vm.awesomeThingToDo(); // Call to real controller method which should call the mock service method. 

     expect(vm.awesomeMessage).toEqual('It is awesome!'); // It works. But ONLY because I wrote the vm.awesomeMessage above. 

    }); 

    }); 
})(); 

Meine app Angular 1.2.28 und Jasmin 2.1.3 (mit Grunt und Karma) verwendet.

UPDATE: Gelöst!

it("Should return an awesome message", function() { 
    // Solved with callback parameter 
    spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function(callback) { 
    return { 
     then: function(callback) { 
     callback({awesomeMessage: 'It is awesome!'}); //callback call works fine! :D 
     } 
    } 
    }); 

Antwort

0

Nein, so würde ich das nicht machen.

Erstens, es ist nicht notwendig, einen Schein-Service zu erstellen: Sie können den echten injizieren und ihn ausspionieren.

Zweitens hat Angular alles, was Sie brauchen, um Versprechungen zu schaffen und sie zu lösen. Keine Notwendigkeit, gefälschte Objekte mit einer gefälschten then() Funktion zu erstellen.

Hier ist, wie ich es tun würde:

describe("Awesome Controller Tests", function() { 

    beforeEach(module('myApp.myModule')); 

    var vm, awesomeService, $q, $rootScope; 

    beforeEach(inject(function($controller, _awesomeService_, _$q_, _$rootScope_) { 
     $q = _$q_; 
     awesomeService = _awesomeService_; 
     $rootScope = _$rootScope_; 
     vm = $controller('Awesome'); 
    })); 

    it("Should return an awesome message", function() { 
     spyOn(awesomeService, "awesomeThingToDo").and.returnValue(
      $q.when({ 
      awesomeMessage: 'awesome message' 
      })); 

     vm.awesomeThingToDo(); 

     // at this time, the then() callback hasn't been called yet: 
     // it's called at the next digest loop, that we will trigger 

     $rootScope.$apply(); 

     // now the then() callback should have been called and initialized 
     // the message in the controller with the message of the promise 
     // returned by the service 

     expect(vm.awesomeMessage).toBe('awesome message'); 
    }); 

}); 

Unrelated Anmerkung: 1.2.28 ist ziemlich alt. Sie sollten auf die neueste Version migrieren.

it("Should return an awesome message", function() { 
    // Solved with callback parameter 
    spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function(callback) { 
    return { 
     then: function(callback) { 
     callback({awesomeMessage: 'It is awesome!'}); //callback call works fine! :D 
     } 
    } 
    }); 

ich einen Rückruf verwendet, um den verspottet Parameter zu übergeben und die reale Umsetzung nennen:

+0

Ich würde die Angular-Version aktualisieren ... Aber ich kann es jetzt nicht tun. :( Aber ich aktualisierte die Frage mit der Lösung.: D Danke! –

+0

Ihre Lösung ist sowohl zu ausführlich, und nicht optimal: das Versprechen soll asynchron sein, aber Ihr Test macht es synchron. Sie sollten das Versprechen vorziehen basierte Lösung, die in meiner Antwort erklärt wird –

+0

Sie haben Recht.Meine Lösung ist nicht gut genug. Ich versuchte, Ihre Lösung anzuwenden, injizieren den Service, mit $ q ... aber ich erhielt diesen Fehler: Fehler: Unerwartet request: GET/my-backend/api/awesome –

1

ich die Frage mit einer möglichen (sehr schlecht) Lösung aktualisiert. : D

Verwandte Themen