2013-03-18 8 views
16

Ich versuche, einen Dienst zu testen, der asynchrone Methoden hat, aber kein Glück haben.Unit testet einen asynchronen Dienst in angularjs

Ich habe versucht, mit Versprechen zu implementieren, indem Sie die $ q Unterstützung in angularjs verwenden.

Jede Hilfe wäre willkommen.

http://jsfiddle.net/9pBze/37/

angular.module('myapp', ['myservice']); 

angular.module('myservice', []).factory('myservice', function($q) { 
    var ls = {}; 

    ls.DoIt = function() { 
    var deferred = $q.defer(); 

    setTimeout(function(){ 
     deferred.resolve(5); 
    },3000); 

    return deferred.promise; 
    } 

    return ls; 

}); 

describe('services', function() { 

    beforeEach(module('myservice')); 

    it("should equal 2", inject(function(myservice) { 
     myservice.DoIt().then(function(returned) { 
      expect(returned).toEqual(2); 
     });   
    })); 
}); 

Antwort

29

Zunächst einmal ist die setTimeout besonders schwierig zu testen, da es schwer zu verspotten zu. Zum Glück hat AngularJS einen Wrapper um es ($timeout), die die gleiche Rolle spielt, aber leicht verspottet werden kann:

ls.DoIt = function() { 
    var deferred = $q.defer(); 

    $timeout(function(){ 
     deferred.resolve(5); 
    },3000); 

    return deferred.promise; 
    } 

Die Mock für $timeout hat uns vergangene Zeit erlaubt auf einfache Weise zu simulieren (mit $timeout.flush()), das bedeutet, dass unsere Tests durchführen können schnell, ohne wirklich darauf zu warten, dass das asynchrone Ereignis abgeschlossen wird (bitte beachten Sie, dass der Produktionscode immer noch die async-API verwendet!).

Die geänderten Tests würden wie folgt aussehen:

it("should equal 5", inject(function(myservice, $timeout) { 

    var valueToVerify; 
    myservice.DoIt().then(function(returned) { 
     valueToVerify = returned; 
    }); 
    $timeout.flush();   
    expect(valueToVerify).toEqual(5); 
})); 

Und schließlich der Arbeits jsFiddle: http://jsfiddle.net/v9L9G/1/

+0

Danke für die detaillierte Erklärung. Der Dienst, den ich schreibe, verwendet IndexedDB und die API ist asynchron und ich habe Schwierigkeiten, wie ich das oben Angeführte anwenden kann, wenn die DoIt-Funktion etwas wie request.onsuccess = function (event) rufen würde { // Tun Sie etwas mit request.result ! deferred.resolve (Ergebnis); }; – user1405779

+0

@Pawel Was passiert, wenn ich einen Controller teste, der 'DoIt'-Mock-Service verwendet, und' DoIt' in diesem Controller in einer Schleife ist, so wird er mehrmals aufgerufen. Wie spüle ich es mehrmals? Ist es nicht möglich, 'DoIt' zu aktivieren? Ich habe versucht, einen asynchronen Spott zu bekommen, um sich selbst zu leeren, indem ich [this] (http://jsfiddle.net/cUEQ8/) darin einfüge, aber das scheint nicht sehr robust zu sein. –

+0

Eigentlich scheint die Geige, die ich oben geteilt habe, erfolgreich einen Spott zu bekommen. Das Problem, das ich festgestellt habe, war woanders ... –

4

Es ist nicht zu Angular selbst beziehen, sondern auf Jasmine async tests.

Wenn Sie eine setTimeout benötigen, verwenden Sie Angular $timeout. Wenn Sie eine genaue Kontrolle über die Ausführung von setTimeout/$ timeout wünschen, verwenden Sie mocked Clock.

+0

Danke für Ihre Hilfe. Ich werde die $ timeout-Dokumentation überprüfen. – user1405779

+0

Der angegebene Link zum Jasmine Async 404ed. Ich denke es ist jetzt http://jasmine.github.io/2.0/introduction.html#section-asynchronous_Support – Stephane

+0

Sie hatten Recht. Danke @StephaneEybert –