用承诺模拟服务,以便在另一个服务中进行测试
Mocking a service with a promise, for testing within another service
我有以下服务(为简单起见减少了):
angular.module('myApp')
.factory('BasicTimerService', function BasicTimerService(PromiseService)
{
var isPlaying = false,
maxEventTime = 0;
return {
start: function()
{
// only "start" once this promise has resolved.
PromiseService.get_promise()
.then(function(value)
{
maxEventTime = PromiseService.maxEventTime();
isPlaying = true;
});
},
get_isPlaying: function() {
return isPlaying;
}
};
});
我想测试一下,maxEventTime只在Promise成功后设置。
我的测试:
describe('Service: BasicTimerService', function () {
beforeEach(module('myApp'));
var mockPromiseService,
BasicTimerService,
$q,
$rootScope,
deferred;
beforeEach(function(){
mockPromiseService = {
maxEventTime: function() {
return 17000;
}
};
module(function($provide){
$provide.value('PromiseService', mockPromiseService);
});
inject(function(_BasicTimerService_, _$q_, _$rootScope_){
BasicTimerService = _BasicTimerService_;
$rootScope = _$rootScope_;
$q = _$q_;
});
mockPromiseService.get_promise = function() {
var deferred = $q.defer();
deferred.resolve('hello world');
return deferred.promise;
};
});
it('should get maxEventTime once the promise is resolved.', function() {
spyOn(mockPromiseService, 'get_promise').and.callThrough();
spyOn(mockPromiseService, 'maxEventTime').and.callThrough();
$rootScope.$apply();
BasicTimerService.start();
expect(mockPromiseService.get_promise).toHaveBeenCalled();
expect(BasicTimerService.get_isPlaying()).toBe(true);
expect(mockPromiseService.maxEventTime).toHaveBeenCalled();
});
});
确定mockPromiseService.get_promise()调用成功(第一个expect()成功),但是BasicTimerService中的.then()子句好像没有调用,想不通为什么。
我试过 $rootScope.$apply();
和 $rootScope.$digest();
都没有用。
关于为什么这不起作用的任何想法?
您的 start()
方法是异步的,因此如果它正常工作,当您检查它的值时 get_isPlaying()
将不会是 true
。您需要等待结果(将 start()
修改为 return Benjamin Gruenbaum 建议的承诺:
it('should get maxEventTime once the promise is resolved.', function(done) {
spyOn(mockPromiseService, 'get_promise').and.callThrough();
spyOn(mockPromiseService, 'maxEventTime').and.callThrough();
$rootScope.$apply();
BasicTimerService.start().then(function () {
expect(mockPromiseService.get_promise).toHaveBeenCalled();
expect(BasicTimerService.get_isPlaying()).toBe(true);
expect(mockPromiseService.maxEventTime).toHaveBeenCalled();
done();
});
});
您也不需要在 get_promise
中使用 $q.defer()
(您几乎不需要使用 $q.defer()
):
mockPromiseService.get_promise = function() {
return $q.when('hello world');
};
注意:根据 $q
文档,您似乎可以使用 $rootScope.$apply()
来获取承诺值以传播到它们的 then
处理程序,但我认为您应该将承诺视为承诺并使用 .then()
等来测试它们。
我有以下服务(为简单起见减少了):
angular.module('myApp')
.factory('BasicTimerService', function BasicTimerService(PromiseService)
{
var isPlaying = false,
maxEventTime = 0;
return {
start: function()
{
// only "start" once this promise has resolved.
PromiseService.get_promise()
.then(function(value)
{
maxEventTime = PromiseService.maxEventTime();
isPlaying = true;
});
},
get_isPlaying: function() {
return isPlaying;
}
};
});
我想测试一下,maxEventTime只在Promise成功后设置。
我的测试:
describe('Service: BasicTimerService', function () {
beforeEach(module('myApp'));
var mockPromiseService,
BasicTimerService,
$q,
$rootScope,
deferred;
beforeEach(function(){
mockPromiseService = {
maxEventTime: function() {
return 17000;
}
};
module(function($provide){
$provide.value('PromiseService', mockPromiseService);
});
inject(function(_BasicTimerService_, _$q_, _$rootScope_){
BasicTimerService = _BasicTimerService_;
$rootScope = _$rootScope_;
$q = _$q_;
});
mockPromiseService.get_promise = function() {
var deferred = $q.defer();
deferred.resolve('hello world');
return deferred.promise;
};
});
it('should get maxEventTime once the promise is resolved.', function() {
spyOn(mockPromiseService, 'get_promise').and.callThrough();
spyOn(mockPromiseService, 'maxEventTime').and.callThrough();
$rootScope.$apply();
BasicTimerService.start();
expect(mockPromiseService.get_promise).toHaveBeenCalled();
expect(BasicTimerService.get_isPlaying()).toBe(true);
expect(mockPromiseService.maxEventTime).toHaveBeenCalled();
});
});
确定mockPromiseService.get_promise()调用成功(第一个expect()成功),但是BasicTimerService中的.then()子句好像没有调用,想不通为什么。
我试过 $rootScope.$apply();
和 $rootScope.$digest();
都没有用。
关于为什么这不起作用的任何想法?
您的 start()
方法是异步的,因此如果它正常工作,当您检查它的值时 get_isPlaying()
将不会是 true
。您需要等待结果(将 start()
修改为 return Benjamin Gruenbaum 建议的承诺:
it('should get maxEventTime once the promise is resolved.', function(done) {
spyOn(mockPromiseService, 'get_promise').and.callThrough();
spyOn(mockPromiseService, 'maxEventTime').and.callThrough();
$rootScope.$apply();
BasicTimerService.start().then(function () {
expect(mockPromiseService.get_promise).toHaveBeenCalled();
expect(BasicTimerService.get_isPlaying()).toBe(true);
expect(mockPromiseService.maxEventTime).toHaveBeenCalled();
done();
});
});
您也不需要在 get_promise
中使用 $q.defer()
(您几乎不需要使用 $q.defer()
):
mockPromiseService.get_promise = function() {
return $q.when('hello world');
};
注意:根据 $q
文档,您似乎可以使用 $rootScope.$apply()
来获取承诺值以传播到它们的 then
处理程序,但我认为您应该将承诺视为承诺并使用 .then()
等来测试它们。