你如何嘲笑一个 angularjs $resource工厂

How do you mock an angularjs $resource factory

本文关键字:angularjs 一个 resource 工厂 何嘲笑      更新时间:2023-09-26

我有一个资源工厂

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来模拟来自服务器的响应,我可以很好地做到这一点。

我的问题是,在对控制器功能进行单元测试时,我想模拟集群对象。 如果我使用的是$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;,但在创建功能中,间谍迷失了

错误:预期是间谍,但获得了功能。

我已经能够让间谍对象用于cluster.$update类型调用,但随后它var cluster = new Clusters();失败并出现"不是函数"错误。

我可以创建一个适用于var cluster = new Clusters();但随后cluster.$update类型调用失败的函数。

我可能在这里混合了术语,但是,是否有一种适当的方法来模拟带有间谍功能的集群,或者是否有充分的理由只使用$httpBackend.expect

看起来我接近了几次,但我想我现在已经弄清楚了。

解决方案是上面的"我也尝试过"部分,但我没有从函数返回间谍对象。

这有效,可以放置在beforeEach(module(beforeEach(inject部分中

步骤 1:使用要测试的任何函数创建间谍对象,并将其分配给测试可访问的变量。

第 2 步:创建一个返回间谍对象的函数。

步骤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'););
    });
  });
});