【问题标题】:How do you mock an angularjs $resource factory你如何模拟 angularjs $resource 工厂
【发布时间】:2015-03-04 15:59:29
【问题描述】:

我有一个资源工厂

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;,但在创建函数中,间谍迷路了

错误:预期是间谍,但得到了函数。

我已经能够让一个间谍对象为 cluster.$update 类型调用工作,但随后它在 var cluster = new Clusters(); 处失败并出现“不是函数”错误。

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

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

【问题讨论】:

    标签: javascript angularjs unit-testing jasmine


    【解决方案1】:

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

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

    这可行,它可以放在beforeEach(module( 或beforeEach(inject 部分中

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

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

    第 3 步:将 spy 对象的属性复制到新函数中。

    clusterSpy = jasmine.createSpyObj('Clusters', ['$save', 'update', 'status']);
    
    clusterSpyFunc = function () {
        return clusterSpy
    };
    
    for(var k in clusterSpy){
        clusterSpyFunc[k]=clusterSpy[k];
    }
    

    第 4 步:将其添加到 $controller 的 beforeEach(inject 部分。

    ClustersController = $controller('ClustersController', {
        $scope: scope,
        Clusters: clusterSpyFunc
    });
    

    在您的测试中,您仍然可以使用

    向方法添加功能
    clusterSpy.$save.and.callFake(function (cb) {
        cb({_id: '1'});
    });
    

    然后检查间谍值

    expect(clusterSpy.$save).toHaveBeenCalled();
    

    这解决了new Clusters() 和Clusters.query 不是函数的问题。现在我可以在不依赖资源工厂的情况下对我的控制器进行单元测试。

    【讨论】:

      【解决方案2】:

      另一种模拟集群服务的方法是:

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

      【讨论】:

      • 要了解函数对象和对象字面量之间的区别,请查看 Douglas Crockford 所著的《Javascript: The good parts》一书,第 20-30 页。通过 Google 搜索可以轻松找到该书的 PDF 版本。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-07
      • 2011-10-07
      • 2013-04-24
      • 1970-01-01
      • 2017-11-12
      • 1970-01-01
      相关资源
      最近更新 更多