如何在 Jasmine 测试中模拟 Angular $q 服务?
How to mock Angular $q service in Jasmine test?
我正在尝试测试一个 Angular 服务,它有两个依赖项,一个依赖于 $q,另一个依赖于 'myService',后者也依赖于 $q。
(function() {
'use strict';
angular.module('myModule').factory('myService', [
'$q',
'apiService',
function($q, apiService) {
var data = null;
function getData() {
var deferred = $q.defer();
if (data === null) {
apiService.get('url').then(function(result) {
data = result;
deferred.resolve(data);
}, function() {
deferred.reject();
});
} else {
deferred.resolve(data);
}
return deferred.promise;
}
return {
getData: getData
};
}
]);
})();
我开始编写如下所示的 Jasmine 测试,但在模拟 $q 时遇到问题。我想将 $q 的真实版本而不是模拟版本注入 'myService' 和 'apiService',但我不确定如何实现。
'use strict';
describe('My service', function() {
var qSpy, apiServiceSpy;
beforeEach(module('myModule'));
beforeEach(function() {
qSpy = jasmine.createSpyObj('qSpy', ['defer']);
apiServiceSpy = jasmine.createSpyObj('apiServiceSpy', ['get']);
apiServiceSpy.get.and.callFake(function() {
var deferred = $q.defer();
deferred.resolve('Remote call result');
return deferred.promise;
});
module(function($provide) {
$provide.value('$q', qSpy);
$provide.value('apiService', apiServiceSpy);
});
});
it('should get data.', inject(function(myService) {
// Arrange
// Act
var data = myService.getData();
// Assert
expect(data).not.toBeNull();
}));
});
编辑
这是基于以下响应的更新测试。我想我的问题是我假设我必须提供 $q.
'use strict';
describe('My service', function() {
var service, apiServiceSpy;
beforeEach(module('myModule'));
beforeEach(function() {
apiServiceSpy = jasmine.createSpyObj('apiServiceSpy', ['get']);
module(function($provide) {
$provide.value('apiService', apiServiceSpy);
});
});
beforeEach(inject(function($q, myService) {
service = myService;
apiServiceSpy.get.and.callFake(function() {
var deferred = $q.defer();
deferred.resolve('Remote call result');
return deferred.promise;
});
}));
it('should get data.', function() {
// Arrange
// Act
var data = service.getData();
// Assert
expect(data).not.toBeNull();
}));
});
您应该在真正的 $q
服务中使用。
var rootScope;
var deferrred;
var fakeMyService = { getData: function() { return deferred.promise}};
beforEach(inject(function($q, $rootScope) {
deferred = $q.defer();
rootScope = $rootScope;
}))
it('should do something', function() {
. . .
deferred.resove(<something>);
$rootScope.$digest();
. . .
expect(. . .)
})
您必须使用 $injector 服务才能获得真正的 angular 服务。
$injector is used to retrieve object instances as defined by provider,
instantiate types, invoke methods, and load modules.
var $q
beforeEach(inject(function($injector) {
$q = $injector.get('$q');
}));
你可以使用真正的$q。需要注意的重要一点是,您应该调用 $scope.$apply()
来解决承诺。
var service;
var $scope;
beforeEach(function() {
angular.mock.module('app', function ($provide) {
$provide.value('apiService', apiServiceSpy);
});
angular.mock.inject(function (_myService_, _$rootScope_) {
service = _myService_;
$scope = _$rootScope_;
});
});
it('works like a charm', function() {
var data;
service.getData().then(function(d) {
data = d;
});
$scope.$apply(); // resolve promise
expect(data).toBeAwesomeData();
});
$scope.$apply()
答案非常好,但我觉得在每个使用 promises 的测试中记住这样做很烦人。所以因为我很懒,所以告诉 $q
像原生 Promises 一样行事。
beforeEach(function() {
module('my.module')
module(function($provide) {
$provide.factory('$q', function() {
// Make $q act like Promise, but with the $q() constructor
function $q(resolve, reject) {
return new Promise(resolve, reject)
}
Object.getOwnPropertyNames(Promise).forEach(function(name) {
$q[name] = Promise[name]
})
return $q
})
})
inject(function(_$controller_, _$q_ /* ... more dependencies */) {
controller = _$controller_('NameOfYourController', {
$q: _$q_,
$scope: {},
})
})
})
我正在尝试测试一个 Angular 服务,它有两个依赖项,一个依赖于 $q,另一个依赖于 'myService',后者也依赖于 $q。
(function() {
'use strict';
angular.module('myModule').factory('myService', [
'$q',
'apiService',
function($q, apiService) {
var data = null;
function getData() {
var deferred = $q.defer();
if (data === null) {
apiService.get('url').then(function(result) {
data = result;
deferred.resolve(data);
}, function() {
deferred.reject();
});
} else {
deferred.resolve(data);
}
return deferred.promise;
}
return {
getData: getData
};
}
]);
})();
我开始编写如下所示的 Jasmine 测试,但在模拟 $q 时遇到问题。我想将 $q 的真实版本而不是模拟版本注入 'myService' 和 'apiService',但我不确定如何实现。
'use strict';
describe('My service', function() {
var qSpy, apiServiceSpy;
beforeEach(module('myModule'));
beforeEach(function() {
qSpy = jasmine.createSpyObj('qSpy', ['defer']);
apiServiceSpy = jasmine.createSpyObj('apiServiceSpy', ['get']);
apiServiceSpy.get.and.callFake(function() {
var deferred = $q.defer();
deferred.resolve('Remote call result');
return deferred.promise;
});
module(function($provide) {
$provide.value('$q', qSpy);
$provide.value('apiService', apiServiceSpy);
});
});
it('should get data.', inject(function(myService) {
// Arrange
// Act
var data = myService.getData();
// Assert
expect(data).not.toBeNull();
}));
});
编辑 这是基于以下响应的更新测试。我想我的问题是我假设我必须提供 $q.
'use strict';
describe('My service', function() {
var service, apiServiceSpy;
beforeEach(module('myModule'));
beforeEach(function() {
apiServiceSpy = jasmine.createSpyObj('apiServiceSpy', ['get']);
module(function($provide) {
$provide.value('apiService', apiServiceSpy);
});
});
beforeEach(inject(function($q, myService) {
service = myService;
apiServiceSpy.get.and.callFake(function() {
var deferred = $q.defer();
deferred.resolve('Remote call result');
return deferred.promise;
});
}));
it('should get data.', function() {
// Arrange
// Act
var data = service.getData();
// Assert
expect(data).not.toBeNull();
}));
});
您应该在真正的 $q
服务中使用。
var rootScope;
var deferrred;
var fakeMyService = { getData: function() { return deferred.promise}};
beforEach(inject(function($q, $rootScope) {
deferred = $q.defer();
rootScope = $rootScope;
}))
it('should do something', function() {
. . .
deferred.resove(<something>);
$rootScope.$digest();
. . .
expect(. . .)
})
您必须使用 $injector 服务才能获得真正的 angular 服务。
$injector is used to retrieve object instances as defined by provider, instantiate types, invoke methods, and load modules.
var $q
beforeEach(inject(function($injector) {
$q = $injector.get('$q');
}));
你可以使用真正的$q。需要注意的重要一点是,您应该调用 $scope.$apply()
来解决承诺。
var service;
var $scope;
beforeEach(function() {
angular.mock.module('app', function ($provide) {
$provide.value('apiService', apiServiceSpy);
});
angular.mock.inject(function (_myService_, _$rootScope_) {
service = _myService_;
$scope = _$rootScope_;
});
});
it('works like a charm', function() {
var data;
service.getData().then(function(d) {
data = d;
});
$scope.$apply(); // resolve promise
expect(data).toBeAwesomeData();
});
$scope.$apply()
答案非常好,但我觉得在每个使用 promises 的测试中记住这样做很烦人。所以因为我很懒,所以告诉 $q
像原生 Promises 一样行事。
beforeEach(function() {
module('my.module')
module(function($provide) {
$provide.factory('$q', function() {
// Make $q act like Promise, but with the $q() constructor
function $q(resolve, reject) {
return new Promise(resolve, reject)
}
Object.getOwnPropertyNames(Promise).forEach(function(name) {
$q[name] = Promise[name]
})
return $q
})
})
inject(function(_$controller_, _$q_ /* ... more dependencies */) {
controller = _$controller_('NameOfYourController', {
$q: _$q_,
$scope: {},
})
})
})