2016-06-13 23 views
0

我有一個webworker爲我做一些工作。 我已經將它包裝到一個服務中,並且此webworker在Promise中執行。使用webworker測試q.defer(),如何防止計時問題?

現在我用Jasmine測試了這個,看起來測試結束後答應了。

這裏的難點在於延遲和webworker在不同的時間點都是異步的。

我試過用異步茉莉花完成setTimeout $ scope。$ apply()。但'deferred.resolve(e.data.filtered)''在所有這些定時器都被掛起之後被調用。

我的角度服務是這樣的:

'use strict'; 

angular.module('app.demographics').service('FilteringService', FilteringService); 
FilteringService.$inject = ['$q']; 

function FilteringService($q) { 
    this.filter = function (dataSet, filters) { 
     var deferred = $q.defer(); 
     var worker = new Worker('my.worker.js'); 
     var filterData = { 
      dataSet: dataSet, 
      filters: filters 
     }; 
     worker.postMessage(filterData); 
     worker.onmessage = function (e) { 
      if (e.data && e.data.ready) { 
       deferred.resolve(e.data.filtered); 
      } 
     }; 
     worker.onerror = function (e) { 
      console.log("something went wrong while filtering: ", e); 
      deferred.reject(e); 
     }; 

     return deferred.promise; 
    }; 
} 

而且我的測試是這樣的,我希望能夠正常工作,但是它永遠不會到來的預期。

'use strict'; 

describe('FilteringService: ', function() { 

    var filteringService, $q, 
     dataSet = [{a: 1, b: 2}, {c: 3, d: 4}], 
     filters = []; 

    beforeEach(function() { 
     module('app.demographics'); 

     inject(function (_$rootScope_, _FilteringService_, _$q_) { 
      filteringService = _FilteringService_; 
      $q = _$q_; 
     }); 
    }); 

    it('should return a promise on filtering', function() { 
     var filteringPromise = filteringService.filter(dataSet, filters); 

     filteringPromise.then(function (data) { 
      expect(data.length).toEqual(dataSet.length); 
     }, function (failure) { 
      fail(failure); 
     }); 
    }); 
}); 

回答

0

正如https://stackoverflow.com/a/37853075/1319998中提到的,原始測試似乎更多的是集成測試而不是單元測試。如果你想這是一個單元測試....


你需要能夠嘲笑工人,所以你沒有測試它做什麼。因此,在服務中,不要直接撥打Worker,您可以撥打$window.Worker,因爲$window可以在測試中輕鬆進行模擬。

app.service('FilteringService', FilteringService); 
FilteringService.$inject = ['$window', '$q', '$rootScope']; 

function FilteringService($window, $q, $rootScope) { 
    this.filter = function (dataSet, filters) { 
    var deferred = $q.defer(); 
    var worker = new $window.Worker('my.worker.js'); 
    ... 

然後在測試中,你可以創建一個嘲笑工人,調用attacted onmessage處理程序,將通過真正的工人被調用,並測試了諾言,然後獲取正確的值解決(我已經離開它因爲只是測試長度,但在真正的測試中,我懷疑你會需要更好的東西)。

describe('FilteringService: ', function() { 

    var $rootScope, filteringService, $q, 
    dataSet = [{a: 1, b: 2}, {c: 3, d: 4}], 
    filters = []; 

    var mockWorker; 
    var mockWindow = { 
    Worker: function() { 
     return mockWorker; 
    } 
    }; 

    beforeEach(function() { 
    module('app.demographics'); 

    module(function($provide) { 
     $provide.value('$window', mockWindow); 
    }); 

    inject(function (_$rootScope_, _FilteringService_, _$q_) { 
     $rootScope = _$rootScope_; 
     filteringService = _FilteringService_; 
     $q = _$q_; 
    }); 

    mockWorker = { 
     postMessage: jasmine.createSpy('onMessage') 
    } 
    }); 

    it('when onmessage from worker called, resolves returned promise with filtered list', function() { 
    expect(mockWorker.postMessage).not.toHaveBeenCalled(); 
    expect(mockWorker.onmessage).not.toEqual(jasmine.any(Function)); 

    var filteringPromise = filteringService.filter(dataSet, filters); 

    expect(mockWorker.postMessage).toHaveBeenCalled(); 
    expect(mockWorker.onmessage).toEqual(jasmine.any(Function)); 

    mockWorker.onmessage({ 
     data: { 
     ready: true, 
     filtered: dataSet 
     } 
    }); 

    var result; 
    filteringPromise.then(function(_result) { 
     result = _result; 
    }); 
    $rootScope.$apply(); 
    expect(result.length).toEqual(dataSet.length); 
    }); 
}); 

注然後需要在測試(而不是服務)$apply,以確保承諾的回調被調用。

你可以看到這個工作在https://plnkr.co/edit/g2q3ZnD8AGZCkgkkEkdj?p=preview

0

我接受這不是最佳的解決方案,更多的可能是黑客,但這是我如何得到茉莉花與Angular合作。我的方法是創建一個函數digestIt,其中完成函數由Jasmine提供,並調用$摘要使用setInterval並返回一個清理函數。

function digestIt($rootScope, done) { 
var intervalId: number,  
    _done = function() { 
     if (angular.isDefined(intervalId)) 
      clearInterval(intervalId); 
     intervalId = null; 
     done(); 
    }, 
    _interval = function() { 
     if (angular.isNumber(intervalId)) { 
      try { 
       $rootScope.$digest(); 
      } catch (e) {     
       _done();    
      } 
     } 
    }, 
    intervalId = setInterval(_interval, 1); 
    return _done; 
} 

下面是使用模式。

describe("MyService ", function() { 
    var $rootScope, 
     $injector 
     ; 

    beforeEach(angular.mock.inject(function (_$rootScope_, _$injector_) { 
     $rootScope = _$rootScope_.$new(); 
     $injector = _$injector_; 
    })); 

    it("My Test", function (done) { 
     var $docs = $injector.get('MyService'), 
      completed = digestIt($rootScope, done) 
      ; 
     $docs.asyncCall().then(function() { 
      /* expect */ 
     }).catch(function() { 
      /* fail */ 
     }).finally(function() { 
      completed(); 
     }); 
    }); 
}); 
0

它看起來(至少在測試環境),$q承諾只得到解決喜好(如,他們的成功/失敗回調調用)時,消化週期被啓動。所以在服務你可以把$rootScope.apply()來觸發此:

worker.onmessage = function (e) { 
    if (e.data && e.data.ready) { 
    $rootScope.$apply(function() { 
     deferred.resolve(e.data.filtered); 
    }); 
    } 
}; 
worker.onerror = function (e) { 
    console.log("something went wrong while filtering: ", e); 
    $rootScope.$apply(function() { 
    deferred.reject(e); 
    }); 
}; 

然後你的測試可以是異步:

it('should return a promise on filtering', function (done) { 
    var filteringPromise = filteringService.filter(dataSet, filters); 

    filteringPromise.then(function (data) { 
    expect(data.length).toEqual(dataSet.length); 
    done(); 
    }, function (failure) { 
    fail(failure); 
    }); 
}); 

這可以在https://plnkr.co/edit/D21EhoCXIbj8R0P9RY40?p=preview


注中可以看出:這可能被歸類爲集成測試而不是單元測試,因爲您正在同時測試FilteringService和您的工作人員。如果您只需進行單元測試,則可以通過嘲笑工作人員避免在FilteringService中添加$rootScope.$apply()。你可能也會做出一個同步測試。

相關問題