2015-09-22 16 views
9

Obecnie próbujemy przetestować nasze usługi kątowe, które wykorzystują obietnice zwracania wartości kontrolerom. Problem polega na tym, że funkcje, które przywiązujemy do. Then, nie są wywoływane w Jasmine.

Odkryliśmy, że dodanie $ rootScope.digest() do funkcji po zwróceniu obietnicy pozwala na zwołanie synchronicznych obietnic, jednak nadal nie działa w przypadku asynchronicznych obietnic.

Kod jakim jest

beforeEach(inject(function (Service, $rootScope) 
    { 
     service = Service; 
     root = $rootScope; 
    })); 

    it('gets all the available products', function (done) 
    { 
     service.getData().then(function (data) 
     { 
      expect(data).not.toBeNull(); 
      expect(data.length).toBeGreaterThan(0); 
      done(); 
     }); 
     root.$digest(); 
    }); 

W tym przypadku obietnica zostanie wywołany w porządku, ale jeśli to jest asynchroniczne nie zostanie wywołana, ponieważ obietnica nie jest gotowy do „trawienia” przez czas korzeniowego. Wywoływana jest funkcja $ digest().

Czy jest jakiś sposób, aby powiedzieć, kiedy obietnica jest po rozwiązaniu, abym mógł nazwać skrót? A może coś, co zrobiłoby to automatycznie? Dzięki;)

Częściowa Służby musimy Test (obsługa błędów usunięte):

var app = angular.module('service', []); 

/** 
* Service for accessing data relating to the updates/downloads 
* */ 
app.factory('Service', function ($q) 
{ 
    ... init 

    function getData() 
    { 
     var deffered = $q.defer(); 

     var processors = [displayNameProc]; 

     downloads.getData(function (err, data) 
     { 
      chain.process(processors, data, function (err, processedData) 
      { 
       deffered.resolve(processedData); 
      }); 
     }); 

     return deffered.promise; 
    } 
    ... 

W przypadku, gdy istnieje problem jest wtedy, gdy service.getData jest asynchroniczny obietnica zostanie rozwiązany ale funkcja dołączone do tej obietnicy z kodu testowego nie jest wywoływana, ponieważ root. $ digest już został wywołany. Nadzieję, że to daje więcej informacji

Obejście

var interval; 
beforeEach(inject(function ($rootScope) 
{ 
    if(!interval) 
    { 
     interval = function() 
     { 
      $rootScope.$digest(); 
     }; 
     window.setInterval(interval, /*timing*/); 
    } 
})); 

skończyło się za pomocą obejścia powyżej zadzwonić do likwidacji wielokrotnie więc każda warstwa obietnicy będzie miał okazję do uruchomienia ... jej nie jest idealny i dość ale to działa w teście ...

+0

Czy szyderczy swoje usługi i dlatego zwrócony obietnicy? – CainBot

+0

Nie Próbowałem przetestować rzeczywistą usługę. – BrendanM

+0

Jeśli testujesz rzeczywistą usługę i wewnętrznie używasz usługi $ http, to sądzę, że skrócone zostanie połączenie przez angularjs, gdy zdalne wywołanie się powiedzie lub się nie powiedzie. – CainBot

Odpowiedz

0

należy Najedź twierdzi poza ówczesny wierzę:

beforeEach(inject(function (Service, $rootScope) 
{ 
    service = Service; 
    root = $rootScope; 
})); 

it('gets all the available products', function (done) 
{ 
    var result; 
    service.getData().then(function (data) 
    { 
     result = data; 
     done(); 
    }); 

    root.$digest(); 

    expect(result).not.toBeNull(); 
    expect(result .length).toBeGreaterThan(0); 
}); 

Aktualizacja

Rozwidla się plunkr z poniższego komentarza, aby pokazać, jak przetestować połączenie asynchroniczne.

Dodano $ timeout na służbie:

var app = angular.module('bad-service', []); 

app.service('BadService', function ($q, $interval, $timeout) 
{ 
    this.innerPromise = function(){ 
     var task = $q.defer(); 
     console.log('Inside inner promise'); 

     $timeout(function() { 
     console.log('Inner promise timer fired'); 
     task.resolve(); 
     }, 500); 
     // $interval(function(){ 

     // }, 500, 1); 
     return task.promise; 
    } 
}); 

A ja spłukiwania $ timeout przed twierdząc:

beforeEach(module('test')); 


describe('test', function(){ 
    var service, root, $timeout; 

    beforeEach(inject(function (Service, $rootScope, _$timeout_) 
    { 
     $timeout = _$timeout_; 
     console.log('injecting'); 
     service = Service; 
     root = $rootScope; 
    })); 

    /** 
    Test one 
    */ 
    it('Should work', function(done){ 
     console.log('Before service call'); 
     service.outerPromise().then(function resolve(){ 
     console.log('I should be run!'); 
     expect(true).toBeTruthy(); 
     done(); 
     }); 
     // You will have to use the correct "resolve" here. For example when using: 
     // $httpbackend - $httpBackend.flush(); 
     // $timeout- $timeout.flush(); 
     $timeout.flush(); 

     console.log('After service call'); 
    }); 
}); 
+0

Niestety to przyniesie efekt w naszym przypadku, ponieważ getData jest async, co oznacza, że ​​wynik będzie mieć wartość null przed osiągnięciem pierwszego oczekiwanego wyniku (result) .not.toBeNull(); – BrendanM

+0

To rozwiązanie jest specyficzne dla połączeń asynchronicznych; czy sprawdziłeś, czy to nie działa? Połączenie "done();" i "root. $ digest();" upewni się, że twoje połączenie asynchroniczne zostanie rozstrzygnięte przed wywołaniem oczekiwań. –

+0

Tak przepraszam, widzę, że teraz źle dasz temu strzał – BrendanM