我有一个网络工作者为我做了一些工作。 我把它包装成了一个服务,这个webworker在Promise中执行。
现在我用Jasmine进行测试,看来承诺在测试完成后返回。
这里的难点在于延迟和网络工作者在不同的时间点都是异步的。
我尝试过使用done,setTimeout,$ scope。$ apply()执行async jasmine。但是' 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 :(得分:0)
我接受这不是最佳解决方案,可能更多是黑客攻击,但这就是我让Jasmine与Angular合作的方式。我的方法是创建一个函数 digestIt ,它接受Jasmine提供的 done 函数,并使用setInterval调用$ digest并返回一个清理函数。
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();
});
});
});
答案 1 :(得分: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
和您的工作人员。如果您只进行单元测试,则可以通过模拟工作人员来避免在$rootScope.$apply()
中添加FilteringService
。您可能也可以进行同步测试。
答案 2 :(得分: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');
...
然后在测试中你可以创建一个模拟的worker,调用将由真正的worker调用的att onmessage
处理程序,然后测试promise是否以正确的值解析(我已经离开了只是测试长度,但在真正的测试中,我怀疑你需要更好的东西)。
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
(而不是服务),以确保调用promise回调。