[英]How to unit test (using Jasmine) a function in a controller which calls a factory service which returns a promise
In the below SampleController , how do I unit test that postAttributes function calls sampleService.updateMethod . 在下面的SampleController中 ,我如何对postAttributes函数调用sampleService.updateMethod进行单元测试。 I'm having trouble since the updateMethod returns promise.
我遇到麻烦,因为updateMethod返回了promise。
angular.module('sampleModule')
.controller('SampleController', SampleController);
SampleController.$inject =['sampleService'];
function SampleController(sampleService){
this.postAttributes = function() {
sampleService.updateMethod(number,attributes)
.then(function(response){
//do something on successful update
},function(response){
//do something on unsuccessful update
});
};
}
Here is the factory service that I have: 这是我的工厂服务:
angular.module('sampleModule')
.factory('sampleService', sampleService);
sampleService.$inject = ['$http'];
function sampleService($http) {
return {
getMethod: function(acctNumber){
return $http({
method: 'GET',
url: //api endpoint
});
},
updateMethod: function(number, attributes){
return $http({
method: 'PUT',
url: //api endpoint,
data: //payload
});
}
};
}
I would like to mock the factory service in the controller spec rather than injecting the actual service directly into $controller, since most of unit testing guidelines specify to test a unit under isolation. 我想在控制器规范中模拟工厂服务,而不是直接将实际服务注入$ controller,因为大多数单元测试指南都指定在隔离下测试一个单元。
Sample controller spec: 样本控制器规范:
describe('SampleController Test', function(){
var $controller;
var service;
beforeEach(angular.mock.module('sampleModule'));
beforeEach(angular.mock.inject(function(_$controller_){
$controller = _$controller_;
}));
it('Testing $scope variable', function(){
var sampleController = $controller('SampleController', {
sampleService: service, //mocked factory service
});
sampleController.postAttributes(); //calling the function first
//here I would like to make an assertion to check if
//sampleService.updateMethod has been called with certain parameters
//how do I do that??
});
});
Googled around and found a solution to mock the factory service and followed promise creation approach like @TehBeardedOne and made it to return it from the mocked service. 谷歌搜索并找到一个模拟工厂服务的解决方案,并遵循像@TehBeardedOne这样的承诺创建方法,并让它从模拟服务返回它。
describe('SampleController', function(){
var mockService, controller, deferred, $rootScope;
beforeEach(function(){
angular.mock.module('sampleModule');
angular.mock.module(function($provide){
$provide.factory('sampleService', function($q){
function updateMethod(acct, attr){
deferred = $q.defer();
return deferred.promise;
}
return{updateMethod: updateMethod};
});
});
angular.mock.inject(function($controller, sampleService, _$rootScope_){
$rootScope = _$rootScope_;
mockService = sampleService;
spyOn(mockService, 'updateMethod').and.callThrough();
controller =$controller('SampleController', {
sampleService: mockService,
})
});
});
it('postAttributes function should call updateMethod', function(){
controller.postAttributes();
expect(mockService.updateMethod).toHaveBeenCalled();
expect(mockService.updateMethod).toHaveBeenCalledWith(controller.accountNumber, controller.attributes);
});
it('postAttributes success block', function(){
controller.postAttributes();
var res = {
data: '2323'
}
deferred.resolve(res);
$rootScope.$digest();
expect(//something in success block).toBe(something);
});
it('postAttributes failure block', function(){
controller.postAttributes();
var res = {
data: '9898'
}
deferred.reject(res);
$rootScope.$digest();
expect(controller.lame).toBe('dont type shit');
});
});
I've mocked the sampleService with $provider service and made updateMethod to return a promise using $q . 我嘲笑与$提供商的服务sampleService并提出updateMethod返回使用$ Q的承诺。 Later on you can resolve or reject the promise and test the success and failure blocks in individual it blocks.
稍后,您可以解决或拒绝承诺并测试其阻止的单个成功和失败块。
You will need to use a spy to check that the function is being called. 您将需要使用间谍来检查是否正在调用该函数。 There are several ways to go about doing this as I'm sure you have probably already noticed.
有几种方法可以做到这一点,因为我相信你可能已经注意到了。 You also need to inject
$q
and $rootScope
as you will need to loop the digest cycle in order to return the promise. 你还需要注入
$q
和$rootScope
因为你需要循环摘要周期才能返回promise。
In fact, if all you want to do is check that the function is being called a simple spy will work just fine. 事实上,如果你想做的就是检查函数是否被调用,一个简单的间谍就可以了。 You don't even have to return the promise.
你甚至不必退还承诺。 That being said, if you want to continue on through the
postAttributes()
function and test other stuff inside this function then you will need to return the promise to do that. 话虽这么说,如果你想继续通过
postAttributes()
函数并测试这个函数中的其他东西,那么你将需要返回承诺来做到这一点。 Here is the approach that works well for me. 这是适合我的方法。 Something like this should work.
这样的事情应该有效。
describe('SampleController Test', function(){
var $controller;
var service;
var $q;
var $rootScope;
var updateMethodDeferred;
var ctrl;
var mockHttpObject;
beforeEach(angular.mock.module('sampleModule'));
beforeEach(angular.mock.inject(function(_sampleService_, _$q_, _$rootScope_, _$controller_){
service = _sampleService_;
$q = _$q_;
$rootScope = _$rootScope_;
$controller = _$controller_;
mockHttpObject = {
//add mock properties here
}
updateMethodDeferred = $q.defer();
spyOn(service, 'updateMethod').and.returnValue(updateMethodDeferred.promise);
ctrl = $controller('SampleController', {
sampleService: service, //mocked factory service
});
}));
it('Testing $scope variable', function(){
ctrl.postAttributes();
expect(service.updateMethod).toHaveBeenCalled();
});
it('Should do something after promise is returned', function(){
ctrl.postAttributes();
updateMethodDeferred.resolve(mockHttpObject);
$rootScope.$digest();
expect(/*something that happened on successful update*/).toBe(/*blah*/)
});
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.