你如何模拟一个 angularjs $resource 工厂
How do you mock an angularjs $resource factory
我有一个资源工厂
angular.module('mean.clusters').factory('Clusters', ['$resource',
function($resource) {
return $resource('clusters/:clusterId/:action', {
clusterId: '@_id'
}, {
update: {method: 'PUT'},
status: {method: 'GET', params: {action:'status'}}
});
}]);
和控制器
angular.module('mean.clusters').controller('ClustersController', ['$scope',
'$location', 'Clusters',
function ($scope, $location, Clusters) {
$scope.create = function () {
var cluster = new Clusters();
cluster.$save(function (response) {
$location.path('clusters/' + response._id);
});
};
$scope.update = function () {
var cluster = $scope.cluster;
cluster.$update(function () {
$location.path('clusters/' + cluster._id);
});
};
$scope.find = function () {
Clusters.query(function (clusters) {
$scope.clusters = clusters;
});
};
}]);
我正在编写我的单元测试,我发现的每个示例都使用某种形式的 $httpBackend.expect
来模拟来自服务器的响应,我可以做到这一点。
我的问题是,在对我的控制器功能进行单元测试时,我想模拟 Clusters 对象。如果我正在使用 $httpBackend.expect
,并且我在我的工厂中引入了一个错误,我控制器中的每个单元测试都会失败。
我想测试 $scope.create
只测试 $scope.create
而不是我的工厂代码。
我尝试在测试的 beforeEach(module('mean', function ($provide) {
部分添加提供程序,但我似乎做不对。
我也试过了
clusterSpy = function (properties){
for(var k in properties)
this[k]=properties[k];
};
clusterSpy.$save = jasmine.createSpy().and.callFake(function (cb) {
cb({_id: '1'});
});
并在 before(inject
中设置 Clusters = clusterSpy;
但在创建函数中,间谍会丢失
Error: Expected a spy, but got Function.
我已经能够让间谍对象为 cluster.$update
类型调用工作,但随后它在 var cluster = new Clusters();
处失败并出现 'not a function' 错误。
我可以创建一个适用于 var cluster = new Clusters();
但对 cluster.$update
类型调用失败的函数。
我可能在这里混用了术语,但是,是否有适当的方法来模拟具有函数间谍的集群,或者是否有充分的理由只使用 $httpBackend.expect
?
看起来我已经接近几次了,但我想我现在想通了。
解决方案是上面的 'I also tried' 部分,但我没有从函数返回间谍对象。
这有效,它可以放在 beforeEach(module(
或 beforeEach(inject
部分
第 1 步:使用您要测试的任何函数创建间谍对象,并将其分配给您的测试可以访问的变量。
第 2 步:创建一个 returns 间谍对象的函数。
第 3 步:将间谍对象的属性复制到新函数中。
clusterSpy = jasmine.createSpyObj('Clusters', ['$save', 'update', 'status']);
clusterSpyFunc = function () {
return clusterSpy
};
for(var k in clusterSpy){
clusterSpyFunc[k]=clusterSpy[k];
}
第 4 步:将其添加到 beforeEach(inject
部分的 $controller。
ClustersController = $controller('ClustersController', {
$scope: scope,
Clusters: clusterSpyFunc
});
在您的测试中,您仍然可以使用
向方法添加功能
clusterSpy.$save.and.callFake(function (cb) {
cb({_id: '1'});
});
然后检查间谍值
expect(clusterSpy.$save).toHaveBeenCalled();
这解决了 new Clusters()
和 Clusters.query
不是函数的问题。现在我可以在不依赖资源工厂的情况下对我的控制器进行单元测试。
另一种模拟集群服务的方法是:
describe('Cluster Controller', function() {
var location, scope, controller, MockClusters, passPromise, q;
var cluster = {_id : '1'};
beforeEach(function(){
// since we are outside of angular.js framework,
// we inject the angujar.js services that we need later on
inject(function($rootScope, $controller, $q) {
scope = $rootScope.$new();
controller = $controller;
q = $q;
});
// let's mock the location service
location = {path: jasmine.createSpy('path')};
// let's mock the Clusters service
var MockClusters = function(){};
// since MockClusters is a function object (not literal object)
// we'll need to use the "prototype" property
// for adding methods to the object
MockClusters.prototype.$save = function(success, error) {
var deferred = q.defer();
var promise = deferred.promise;
// since the Clusters controller expect the result to be
// sent back as a callback, we register the success and
// error callbacks with the promise
promise.then(success, error);
// conditionally resolve the promise so we can test
// both paths
if(passPromise){
deferred.resolve(cluster);
} else {
deferred.reject();
}
}
// import the module containing the Clusters controller
module('mean.clusters')
// create an instance of the controller we unit test
// using the services we mocked (except scope)
controller('ClustersController', {
$scope: scope,
$location: location,
Clusters: MockClusters
});
it('save completes successfully', function() {
passPromise = true;
scope.save();
// since MockClusters.$save contains a promise (e.g. an async call)
// we tell angular to process this async call before we can validate
// the response
scope.$apply();
// we can call "toHaveBeenCalledWith" since we mocked "location.path" as a spy
expect(location.path).toHaveBeenCalledWith('clusters/' + cluster._id););
});
it('save doesn''t complete successfully', function() {
passPromise = false;
scope.save();
// since MockClusters.$save contains a promise (e.g. an async call)
// we tell angular to process this async call before we can validate
// the response
scope.$apply();
expect(location.path).toHaveBeenCalledWith('/error'););
});
});
});
我有一个资源工厂
angular.module('mean.clusters').factory('Clusters', ['$resource',
function($resource) {
return $resource('clusters/:clusterId/:action', {
clusterId: '@_id'
}, {
update: {method: 'PUT'},
status: {method: 'GET', params: {action:'status'}}
});
}]);
和控制器
angular.module('mean.clusters').controller('ClustersController', ['$scope',
'$location', 'Clusters',
function ($scope, $location, Clusters) {
$scope.create = function () {
var cluster = new Clusters();
cluster.$save(function (response) {
$location.path('clusters/' + response._id);
});
};
$scope.update = function () {
var cluster = $scope.cluster;
cluster.$update(function () {
$location.path('clusters/' + cluster._id);
});
};
$scope.find = function () {
Clusters.query(function (clusters) {
$scope.clusters = clusters;
});
};
}]);
我正在编写我的单元测试,我发现的每个示例都使用某种形式的 $httpBackend.expect
来模拟来自服务器的响应,我可以做到这一点。
我的问题是,在对我的控制器功能进行单元测试时,我想模拟 Clusters 对象。如果我正在使用 $httpBackend.expect
,并且我在我的工厂中引入了一个错误,我控制器中的每个单元测试都会失败。
我想测试 $scope.create
只测试 $scope.create
而不是我的工厂代码。
我尝试在测试的 beforeEach(module('mean', function ($provide) {
部分添加提供程序,但我似乎做不对。
我也试过了
clusterSpy = function (properties){
for(var k in properties)
this[k]=properties[k];
};
clusterSpy.$save = jasmine.createSpy().and.callFake(function (cb) {
cb({_id: '1'});
});
并在 before(inject
中设置 Clusters = clusterSpy;
但在创建函数中,间谍会丢失
Error: Expected a spy, but got Function.
我已经能够让间谍对象为 cluster.$update
类型调用工作,但随后它在 var cluster = new Clusters();
处失败并出现 'not a function' 错误。
我可以创建一个适用于 var cluster = new Clusters();
但对 cluster.$update
类型调用失败的函数。
我可能在这里混用了术语,但是,是否有适当的方法来模拟具有函数间谍的集群,或者是否有充分的理由只使用 $httpBackend.expect
?
看起来我已经接近几次了,但我想我现在想通了。
解决方案是上面的 'I also tried' 部分,但我没有从函数返回间谍对象。
这有效,它可以放在 beforeEach(module(
或 beforeEach(inject
部分
第 1 步:使用您要测试的任何函数创建间谍对象,并将其分配给您的测试可以访问的变量。
第 2 步:创建一个 returns 间谍对象的函数。
第 3 步:将间谍对象的属性复制到新函数中。
clusterSpy = jasmine.createSpyObj('Clusters', ['$save', 'update', 'status']);
clusterSpyFunc = function () {
return clusterSpy
};
for(var k in clusterSpy){
clusterSpyFunc[k]=clusterSpy[k];
}
第 4 步:将其添加到 beforeEach(inject
部分的 $controller。
ClustersController = $controller('ClustersController', {
$scope: scope,
Clusters: clusterSpyFunc
});
在您的测试中,您仍然可以使用
向方法添加功能clusterSpy.$save.and.callFake(function (cb) {
cb({_id: '1'});
});
然后检查间谍值
expect(clusterSpy.$save).toHaveBeenCalled();
这解决了 new Clusters()
和 Clusters.query
不是函数的问题。现在我可以在不依赖资源工厂的情况下对我的控制器进行单元测试。
另一种模拟集群服务的方法是:
describe('Cluster Controller', function() {
var location, scope, controller, MockClusters, passPromise, q;
var cluster = {_id : '1'};
beforeEach(function(){
// since we are outside of angular.js framework,
// we inject the angujar.js services that we need later on
inject(function($rootScope, $controller, $q) {
scope = $rootScope.$new();
controller = $controller;
q = $q;
});
// let's mock the location service
location = {path: jasmine.createSpy('path')};
// let's mock the Clusters service
var MockClusters = function(){};
// since MockClusters is a function object (not literal object)
// we'll need to use the "prototype" property
// for adding methods to the object
MockClusters.prototype.$save = function(success, error) {
var deferred = q.defer();
var promise = deferred.promise;
// since the Clusters controller expect the result to be
// sent back as a callback, we register the success and
// error callbacks with the promise
promise.then(success, error);
// conditionally resolve the promise so we can test
// both paths
if(passPromise){
deferred.resolve(cluster);
} else {
deferred.reject();
}
}
// import the module containing the Clusters controller
module('mean.clusters')
// create an instance of the controller we unit test
// using the services we mocked (except scope)
controller('ClustersController', {
$scope: scope,
$location: location,
Clusters: MockClusters
});
it('save completes successfully', function() {
passPromise = true;
scope.save();
// since MockClusters.$save contains a promise (e.g. an async call)
// we tell angular to process this async call before we can validate
// the response
scope.$apply();
// we can call "toHaveBeenCalledWith" since we mocked "location.path" as a spy
expect(location.path).toHaveBeenCalledWith('clusters/' + cluster._id););
});
it('save doesn''t complete successfully', function() {
passPromise = false;
scope.save();
// since MockClusters.$save contains a promise (e.g. an async call)
// we tell angular to process this async call before we can validate
// the response
scope.$apply();
expect(location.path).toHaveBeenCalledWith('/error'););
});
});
});