2013-07-02 6 views
43

Mam funkcję wewnątrz jednej z moich usług kątowych, którą chciałbym wywoływać wielokrotnie w regularnych odstępach czasu. Chciałbym to zrobić za pomocą $ timeout. Wygląda to mniej więcej tak:Testowanie jednostki Usługa kątowa korzystająca z opcji Timeout z Jockine Mock Clock

var interval = 1000; // Or something 

var _tick = function() { 
    $timeout(function() { 
     doStuff(); 
     _tick(); 
    }, interval); 
}; 

_tick(); 

jestem zakłopotany, jak ten z testów jednostkowych Jasmine w tej chwili - jak to zrobić? Jeśli używam $timeout.flush(), wywołania funkcji pojawiają się w nieskończoność. Jeśli użyję fałszywego zegara Jasmine, to wydaje się, że nie ma to wpływu na $timeout. Zasadniczo, jeśli mogę dostać tej pracy, powinien być dobry, aby przejść:

describe("ANGULAR Manually ticking the Jasmine Mock Clock", function() { 
    var timerCallback, $timeout; 

    beforeEach(inject(function($injector) { 
     $timeout = $injector.get('$timeout'); 
     timerCallback = jasmine.createSpy('timerCallback'); 
     jasmine.Clock.useMock(); 
    })); 

    it("causes a timeout to be called synchronously", function() { 
     $timeout(function() { 
      timerCallback(); 
     }, 100); 
     expect(timerCallback).not.toHaveBeenCalled(); 
     jasmine.Clock.tick(101); 
     expect(timerCallback).toHaveBeenCalled(); 
    }); 
}); 

Te dwa warianty pracy, ale nie może mi pomóc:

describe("Manually ticking the Jasmine Mock Clock", function() { 
    var timerCallback; 

    beforeEach(function() { 
     timerCallback = jasmine.createSpy('timerCallback'); 
     jasmine.Clock.useMock(); 
    }); 

    it("causes a timeout to be called synchronously", function() { 
     setTimeout(function() { 
      timerCallback(); 
     }, 100); 
     expect(timerCallback).not.toHaveBeenCalled(); 
     jasmine.Clock.tick(101); 
     expect(timerCallback).toHaveBeenCalled(); 
    }); 
}); 

describe("ANGULAR Manually flushing $timeout", function() { 
    var timerCallback, $timeout; 

    beforeEach(inject(function($injector) { 
     $timeout = $injector.get('$timeout'); 
     timerCallback = jasmine.createSpy('timerCallback'); 
    })); 

    it("causes a timeout to be called synchronously", function() { 
     $timeout(function() { 
      timerCallback(); 
     }, 100); 
     expect(timerCallback).not.toHaveBeenCalled(); 
     $timeout.flush(); 
     expect(timerCallback).toHaveBeenCalled(); 
    }); 
}); 

Z góry dzięki!

+0

Spróbuj wstrzykiwanie '$ rootScope' i wywołanie' $ rootScope. Zastosować $() 'po naciśnięciu zegara do przodu. –

Odpowiedz

51

Nie twórz testu Async używając zegara Jasmine. Zamiast tego użyj $timeout.flush(), aby synchronicznie utrzymać przepływ testu. Konfiguracja może być nieco trudna, ale gdy ją zdobędziesz, twoje testy będą szybsze i bardziej kontrolowane.

Oto przykład testu, który robi to za pomocą tego podejścia: https://github.com/angular/angular.js/blob/master/test/ngAnimate/animateSpec.js#L618

+0

Twoja odpowiedź rozwiązuje problem, jak również powtarza ważną zasadę: testy jednostkowe zawsze powinny być synchroniczne. – eitanfar

44

@ odpowiedź matsko doprowadził mnie w dół właściwą drogę. Pomyślałem, że opublikuję moje "kompletne" rozwiązanie, aby ułatwić znalezienie odpowiedzi.

Rzecz przetestować

angular.module("app").service("MyService", function() { 
    return { 
     methodThatHasTimeoutAndReturnsAPromise: function($q, $timeout) { 
      var deferred = $q.defer(); 
      $timeout(function() { 
       deferred.resolve(5); 
      }, 2000); 
      return deferred.promise; 
     } 
    }; 
}); 

Test

describe("MyService", function() { 
    var target, 
     $timeout; 
    beforeEach(inject(function(_$timeout_, MyService) { 
     $timeout = _$timeout_; 
     target = MyService; 
    })); 
    beforeEach(function(done) { 
     done(); 
    }); 
    it("equals 5", function(done) { 
     target.methodThatHasTimeoutAndReturnsAPromise().then(function(value) { 
      expect(value).toBe(5); 
      done(); 
     }); 
     $timeout.flush(); 
    }); 
}); 
+0

Czy "testowanie()" w teście "równa się 5" nie jest zbędne? '$ timeout.flush()' będzie synchronicznie wywoływał wszystkie oczekujące zdarzenia zarejestrowane w $ timeout, co spowoduje, że obietnica rozwiąże się, co natychmiast wywoła 'expect()'. –

+0

Nie jestem do końca pewien. Może jednak warto spróbować. – Beez

+2

@AviCherry Bez parametru zwrotnego 'done', kod wywoła flush, ale natychmiast rozważenie testu, ponieważ nie wie, że wywołano wywołanie asynchroniczne. Z 'done', upewnij się, że test nie zostanie zakończony, dopóki nie zostanie wywołane' done'. "Oczekiwanie" nie zakończy testu. W teście możesz mieć wiele oczekiwań. Także, jeśli podasz 'done', ale nigdy go nie uruchomisz, twój test się nie powiedzie, ponieważ Jasmine rzuci coś w rodzaju' wykonanie trwało zbyt długo', ponieważ test nigdy nie zostanie zakończony. Nie pamiętam dokładnej informacji zwrotnej. –

Powiązane problemy