2016-08-31 14 views
5

Jestem całkiem nowy w Angular i pracuję nad przetestowaniem usługi Angular, która uruchamia usługę poziomu API, która owija wiązkę wywołań do usługi REST. Ponieważ działa z żądaniami HTTP, obie części usługi działają z obietnicami i wydaje się, że działają dobrze, ale mam problemy z uzyskaniem jakichkolwiek testów obiecanego działania.Test jaśminowy dla usługi Angular nie rozwiązuje odroczonego połączenia

Odpowiednia część mojego kodu serwisowego (rażące uproszczony) wygląda następująco:

angular.module('my.info') 
.service('myInfoService', function (infoApi, $q) { 
    infoLoaded: false, 
    allInfo: [], 
    getInfo: function() { 
     var defer = $q.defer(); 
     if (infoLoaded) { 
      defer.resolve(allInfo); 
     } else { 
      infoApi.getAllInfo().then(function (newInfo) { 
       allInfo = newInfo; 
       infoLoaded = true; 
       defer.resolve(allInfo); 
      }); 
     } 
     return defer.promise; 
    } 
}); 

Kiedy jestem skonfigurowaniu makiety mam coś takiego:

describe("Info Service ", 
    function() { 
     var infoService, infoRequestApi, $q; 
     beforeEach(module("my.info")); 
     beforeEach(function() { 
      module(function($provide) { 
       infoRequestApi = { 
        requestCount: 0, 
        getAllInfo: function() { 
         var defer = $q.defer(); 
         this.requestCount++; 
         defer.resolve([ "info 1", "info 2" ]); 
         return defer.promise; 
        } 
       }; 
       $provide.value("infoApi", infoRequestApi); 
      }); 
      inject(function(_myInfoService_, _$q_) { 
       infoService = _myInfoService_, 
       $q = _$q_; 
      }); 
     }); 

     it("should not fail in the middle of a test", function(done) { 
      infoService.getInfo().then(function(infoResult) { 
        // expectation checks. 
        expect(true).toBeTrue(); 
       }).finally(done); 
     }); 
    }); 

Wszelkie badania synchroniczne przechodzą W porządku, ale kiedy próbuję uruchomić takie testy, otrzymuję komunikat: Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

Wygląda to tak, jakby coś o sposobie, w jaki Angular.Moc ks obsłuży odroczony wynik powodując jego niepowodzenie. Kiedy przechodzę test, zmienna próbna obiektu defer jest ustawiana poprawnie, ale instrukcja then w usłudze nigdy nie jest wywoływana. Gdzie się mylę?

Odpowiedz

3

Krótka odpowiedź

Trzeba inauguracją cyklu strawienia. Możesz to zrobić za pomocą $rootScope.$apply().

it("should not fail in the middle of a test", inject(function($rootScope) { 
    var actualResult = null; 
    infoService.getInfo() 
    .then(function(infoResult) { actualResult = infoResult; }); 
    $rootScope.$apply(); 
    expect(actualResult).toEqual(["info 1", "info 2"]); 
})); 

Uwaga

nie będę próbować stworzyć fałszywy infoRequestApi usługę tak, jak to robią. Powinieneś także wstrzykiwać tę usługę i śledzić jej funkcje. Na przykład coś takiego:

it("should not fail in the middle of a test", inject(function($rootScope, infoApi, $q) { 
    var deferred = $q.defer(); 
    spyOn(infoApi, 'getAllInfo').and.returnValue(deferred.promise); 
    var actualResult = null; 
    var expectedResult = ["info 1", "info 2"]; 

    infoService.getInfo() 
    .then(function(infoResult) { actualResult = infoResult; }); 

    deferred.resolve(expectedResult); 
    $rootScope.$apply(); 
    expect(actualResult).toBe(expectedResult); 
})); 

refactored

Również kod można uprościć nieco (niesprawdzone, ale blisko co by się spodziewać, aby zobaczyć).

angular.module('my.info') 
    .service('myInfoService', function (infoApi, $q) { 
    return { 
     infoLoaded: false, 
     allInfo: [], 
     getInfo: function() { 
     var self = this; 
     return this.infoLoaded ? $q.resolve(this.allInfo) : 
      infoApi.getAllInfo().then(function (newInfo) { 
      self.allInfo = newInfo; 
      self.infoLoaded = true; 
      }); 
    } 
    }; 
}); 

describe("Info Service ", function() { 
    var infoService, infoApi, $q, $rootScope; 

    beforeEach(module("my.info")); 
    beforeEach(inject(function(_myInfoService_, _$q_ _$rootScope_, _infoApi_) { 
    infoService = _myInfoService_, 
    $q = _$q_; 
    $rootScope = _$rootScope_; 
    infoApi = _infoApi_; 
    }); 

    it("should do something", function() { // update message 
    var deferred = $q.defer(); 
    spyOn(infoApi, 'getAllInfo').and.returnValue(deferred.promise); 
    var actualResult = null; 
    var expectedResult = ["info 1", "info 2"]; 

    infoService.getInfo() 
     .then(function(infoResult) { actualResult = infoResult; }); 

    deferred.resolve(expectedResult); 
    $rootScope.$apply(); 
    expect(actualResult).toBe(expectedResult); 
    }); 
}); 
+0

Dziękuję podwójnie, zarówno za dobrą odpowiedź, jak i sugestie dotyczące refaktoryzacji. Podejrzewałem, że kpiące podejście jest trochę rozwlekłe! Jedyny problem, jaki mam teraz, to to, że wygląda na to, że jeśli mam kilka warstw usługowych, wygląda na to, że muszę wstrzyknąć próbkę każdemu konstruktorowi od samego początku, ale jest to znacznie bardziej przejrzyste i bardziej idiomatyczne. – glenatron

+0

@glenatron Bez problemu. Trzeba tylko kpić z zależności używanych w testowanej usłudze. Nie trzeba kpić z ich zależności, więc kwestia warstw jest dyskusyjna. –

+0

Masz rację, faktycznie miałem problem z programem testującym Chutzpah dla Visual Studio, który powodował fałszywe niepowodzenia. Te same testy sprawdzają się w przypadku innych biegaczy testowych. – glenatron

Powiązane problemy