【问题标题】:how to unit test a ngcontroller with external dependencies in jasmine?如何在 jasmine 中对具有外部依赖项的 ngcontroller 进行单元测试?
【发布时间】:2015-06-29 12:02:28
【问题描述】:

我正在尝试为 angularcontroller 编写业力单元测试,这是控制器。使用此处的文件上传 (https://github.com/nervgh/angular-file-upload):

var c, di;

c = function($scope, FileUploader, UploadHelper, currentUser) {
  var uploader;

  $scope.uploader = uploader = new FileUploader(UploadHelper.getOptions());

  uploader.onSuccessItem = function(item, response, status, headers) {
    //do something
  };
  return this;
};

di = [ '$scope', 'FileUploader', 'UploadHelper', 'currentUser', c];

angular.module('mycontrollers').controller('DocumentsController', di);

我的茉莉花测试是这样的:

describe('Customer: Controller: DocumentsController', function() {
  var $rootScope, DocumentMock, UserMock, documentsController, scope;
  documentsController = $rootScope = scope = DocumentMock = UserMock = null;

  beforeEach(function() {
    module("mycontrollers");
    return inject(function($controller, _$rootScope_, $q) {
      var deferred, documents, fileUploader, uploadHelper, user;
      $rootScope = _$rootScope_;
      scope = $rootScope.$new();
      documents = [
        {
          id: "doc 1",
          documentType: "other"
        }, {
          id: "doc 2",
          documentType: "pdf"
        }, {
          id: "doc 3",
          v: "pdf"
        }
      ];
      user = {
        id: 'user_id',
        username: 'username'
      };
      UserMock = {
        currentUser: function() {
          return user;
        }
      };
      fileUploader = {};
      uploadHelper = {};

      return documentsController = $controller('DocumentsController', {
        documents: documents,
        $scope: scope,
        FileUploader: fileUploader,
        UploadHelper: uploadHelper,
        currentUser: UserMock

      });
    });
  });
  return describe("On creation", function() {
    return it("should assign documents to scope", function() {
      return expect(1).toEqual(1);
    });
  });
});

我有一个名为 QuestionsAttachments 的服务,它有一些依赖项:

s = function(API, $http, $q, $timeout) {
  var QuestionsAttachments;
  return QuestionsAttachments = (function() {
    function QuestionsAttachments(data) {
      angular.extend(this, data);
    }

    QuestionsAttachments["new"] = function(data) {
      return new QuestionsAttachments(data);
    };

    return QuestionsAttachments;

  })();
};

di = ['API', '$http', '$q', '$timeout', s];

这个问题是:我怎样才能模拟出 API ,$http, $q, $timeout ?

这是一个 plunkr 的链接: http://plnkr.co/edit/BT8SJgW6ejcPw6xo1cpI?p=preview

【问题讨论】:

  • 同样的方法,你只需选择 api/service 和一个方法并在其上放置一个 spy。请再看看我的回答。此外,由于您犯了几个错误,因此您 plunkr 无法正常工作。这是为您工作的plunkr
  • 这个 plunkr 是正确的:plnkr.co/edit/BT8SJgW6ejcPw6xo1cpI?p=preview
  • 我不知道你在哪里。在您的 plunkr 中,您尝试注入 someservice 但这不存在。此外,在您的new:function(){} 块中,您正在尝试更新QuestionsAttachements,但这也不存在。您已将工厂函数命名为 lekker,因此通过将其重命名为 QuestionsAttacments 并删除 someservice 的注入,您就可以看到真正的问题。虽然我建议你阅读angulars dependency injection。我在下面给您的答案几乎可以满足您的所有需求。

标签: angularjs unit-testing karma-jasmine


【解决方案1】:

基本上你所做的就是在那个方法上设置一个间谍 服务。 您所要做的就是获得可注射的服务(真正的实现) 以及只是模拟/存根您需要的功能。无需创建自己的 像这样模拟对象:uploadHelper = {}

spyOn(uploadHelper, 'getOptions').andCallFake(function(){
    // return data here
});

// or you could use .Return({ /* some object */})

您可以阅读更多关于茉莉花和间谍的信息,尤其是here

话虽如此,我必须说提供的代码看起来确实有点不完整。 例如,通过提供的示例,您没有创建任何模块,并且您直接使用 getter 语法而不是 angular.module('mycontrollers', []);

其次,在您的测试中,您尝试在控制器实例化期间注入 documents,但这永远不会起作用,因为您的控制器定义中没有可注入参数 'documents'

所以你遇到的问题很可能是由于上面的概述。

无论如何,一个完整的示例,重点是如何在 UploadHelper.getOptions() 上模拟/存根/间谍,可能看起来像这样。这是一个有效的 Plunkr http://plnkr.co/edit/RfQFsaLq8XV2FvMT3xr0?p=preview

实施

angular
    .module('mycontrollers',[])
    .controller('DocumentsController', documentsController);
    
    documentsController.$inject = ['$scope', 'FileUploader', 'UploadHelper', 'currentUser'];
    
    function documentsController($scope, FileUploader, UploadHelper, currentUser){
        var uploader;
        
        $scope.uploader = uploader = new FileUploader(UploadHelper.getOptions());
        
        uploader.onSuccessItem = function(item, response, status, headers){
            // do something
        };
    }

测试

describe('Customer: Controller: DocumentsController', function(){
    beforeEach(function(){
        module('mycontrollers');
        
        inject(function($injector){
            // fetch our dependencies
            this.$controller = $injector.get('$controller');            
            this.FileUploader = $injector.get('FileUploader');
            this.UploadHelper = $injector.get('UploadHelper');
            this.$scope = $injector.get('$rootScope').$new();
            this.user = {
                id: 'user_id',
                username: 'username'
            };
            this.UserMock = {
                currentUser: function(){
                    return user;
                }
            }
        });
    });
    
    function initController(context){
        return context.$controller('DocumentsController', {
                $scope: context.$scope,
                FileUploader: context.FileUploader,
                UploadHelper: context.UploadHelper,
                currentUser: context.UserMock
            });
    }
    
    describe('On creation', function(){
        it('should call UploadHelper.getOptions()', function(){
            // spy on getOptions and when it is getting called, return whatever we've specified
            spyOn(this.UploadHelper, 'getOptions').andReturn({ /* some options */});
            
            // instantiate the controller
            initController(this);
            
            expect(this.UploadHelper.getOptions).toHaveBeenCalled();
            expect(this.$scope.uploader.onSuccessItem).toBeDefined();
        });
    });
});

【讨论】:

  • 您好,我稍微改了一下,我还有另一个服务叫 questionsAttachments(见上文)
【解决方案2】:

这是一个为注入服务创建完整模拟的简短示例。

var mockInjectedProvider;

beforeEach(function() {
  module('myModule');
});

beforeEach(inject(function(_injected_) {
      mockInjectedProvider = mock(_injected_);
    });

    beforeEach(inject(function(_base_) {
      baseProvider = _base_;
    }));

    it("injectedProvider should be mocked", function() {
      mockInjectedProvider.myFunc.andReturn('testvalue');
      var resultFromMockedProvider = baseProvider.executeMyFuncFromInjected();
      expect(resultFromMockedProvider).toEqual('testvalue');
    });

    //mock all service methods
    function mock(angularServiceToMock) {

      for (var i = 0; i < Object.getOwnPropertyNames(angularServiceToMock).length; i++) {
        spyOn(angularServiceToMock, Object.getOwnPropertyNames(angularServiceToMock)[i]);
      }
      return angularServiceToMock;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-03
    • 2014-05-29
    • 2013-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    相关资源
    最近更新 更多