2016-08-31 32 views
5

Angular'e oldukça yeni geldim ve bir grup çağrıyı bir REST servisine yönlendiren bir API seviyesi hizmetini çalıştıran bir Angular hizmetini test etmek için çalışıyorum. HTTP istekleri ile çalıştığı için, hizmetin her iki bölümü de sözlerle çalışıyor ve iyi çalışıyor gibi görünüyor, ancak söz verilen davranışların herhangi bir testini çalıştırmakta zorlanıyorum.Açısal servis için Jasmine testi ertelenmiş aramayı çözmez

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); 
     }); 
    }); 

Herhangi senkron testler geçmektedir: Ben mock kuruyorum zaman

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; 
    } 
}); 

Böyle bir şey var:

benim servis kodu (fena halde basitleştirilmiş) ilgili kısmı aşağıdaki gibidir iyi, ama böyle bir test çalıştırmayı denediğimde bir mesaj alıyorum: Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

Angular.Moc ks ertelenmiş sonucu işler ve başarısız olmasına neden olur. Test boyunca adım attığımda, sahte nesnenin defer değişkeni doğru olarak ayarlanıyor, ancak hizmette then ifadesi hiçbir zaman çağrılmıyor. Nerede yanlış gidiyorum?

cevap

3

Kısa Cevap

Bir özet döngüsü başlaması gerekir. Bunu $rootScope.$apply() ile yapabilirsiniz.

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"]); 
})); 

Not

Sahte infoRequestApi servisi bunu yapıyor şeklini oluşturmaya çalışın olmaz. Bu servisi de enjekte etmeli ve işlevlerini gözetlemelisiniz. Örneğin, böyle bir şey:

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 Ayrıca, kod basitleştirilmiş olabilir biraz (denenmemiş ama görmeyi beklediğiniz yakın).

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

İyi yanıtlar ve tekrar öneri önerileri için iki katı teşekkür ederiz. Bu alaycı yaklaşımın uzun soluklu olduğundan şüphelendim! Şu anda sahip olduğum tek sorun, sanki bir çift hizmet katmanına sahip olsam sanki her kurucuya doğru bir aldatmacayı enjekte etmem gerekiyormuş gibi gözüküyormuş gibi görünüyor, ama bu çok daha düzenli ve daha idiyomik. – glenatron

+0

@glenatron Sorun değil. Sadece test edilen serviste kullanılan bağımlılıkları alay etmeniz gerekir. Bağımlılıklarıyla dalga geçmenize gerek yok, bu yüzden katman sorunu tartışılıyor. –

+0

Doğru, Visual Studio için Chutzpah test koşucu ile bir sorun haline geliyordum, bu da sahte hatalara neden oluyordu. Aynı testler, diğer test koşucuları altında iyi çalışıyor. – glenatron