如何在jasmine中对具有外部依赖关系的ngcontroller进行单元测试?

时间:2015-01-30 05:24:04

标签: angularjs unit-testing karma-jasmine

我正在尝试为angularcontroller编写一个karma单元测试,这是控制器。使用此处的文件上传(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

2 个答案:

答案 0 :(得分:0)

  

基本上你所做的就是设置间谍方法   服务。   你所要做的就是获得有害的服务(真正的实施)   并且只需要模拟/存根你需要的功能。无需创建自己的   像这样的模拟对象: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();
        });
    });
});

答案 1 :(得分:0)

以下是为注入的服务创建完整模拟的简短示例。

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