angularjs - 测试使用数据表的 Angular Controller - 模拟 DTOptionsBuilder 和 DT ColumnBuilder

标签 angularjs unit-testing datatable jasmine

我正在尝试为主要从服务器创建值的数据表的 Angular Controller 编写测试。我试过 mock DTOptionsBuilder 和 DTColumnBuilder 但这似乎不起作用。我得到错误:

'undefined' 不是一个对象(评估 'DTOptionsBuilder.fromFnPromise(function(){
返回 MarketsFactory.getAll();
})
.withDataProp')

这是 Controller 代码:

.controller('MarketsCtrl', function($scope, $compile, $state, MarketsFactory, 

DTOptionsBuilder, DTColumnBuilder) {

    $scope.edit = function(data){
        $state.go('admin.market', {id:data});
    };

    //DATATABLES CONFIGURATIONS

    $scope.dtInstance = {};

  $scope.dtOptions = DTOptionsBuilder.fromFnPromise(function(){
        return MarketsFactory.getAll();
    })
        .withDataProp('data.data')
        .withOption('createdRow', function(row, data, dataIndex) {
       $compile(angular.element(row).contents())($scope);
    })
        .withTableTools('http://cdn.datatables.net/tabletools/2.2.2/swf/copy_csv_xls_pdf.swf')
    .withTableToolsButtons([
        'copy',
        'print', {
            'sExtends': 'collection',
            'sButtonText': 'Save',
            'aButtons': ['csv', 'xls', 'pdf']
        }
    ])
    .withBootstrap()
    .withBootstrapOptions({
      TableTools: {
        classes: {
          container: 'btn-group right',
          buttons: {
              normal: 'btn btn-outline btn-default btn-sm'
          }
        }
     }
   });

    $scope.dtColumns = [
    DTColumnBuilder.newColumn('shortName').withTitle('Short Name').withClass('dt-left'),
    DTColumnBuilder.newColumn('name').withTitle('Name').withClass('dt-left'),
    DTColumnBuilder.newColumn('timezone').withTitle('Time Zone').withClass('dt-left'),
    DTColumnBuilder.newColumn('id').renderWith(function(data, type, full) {
        return '<a ng-click="edit(\'' + data + '\')">Edit</a>';
    })];
    })

和测试文件:
describe('Controller: MarketsCtrl', function () {
  var scope, $state, DTOptionsBuilder, DTColumnBuilder;

  beforeEach(function(){
    var mockState = {};
    var mockDTOptionsBuilder = {};
    var mockDTColumnBuilder = {};

    module('app', function($provide) {
      $provide.value('$state', mockState);
      $provide.value('DTOptionsBuilder', mockDTOptionsBuilder);
      $provide.value('DTColumnBuilder', mockDTColumnBuilder);
    });

    inject(function() {

      mockState.go = function(target) {
        return target;
      };

      mockDTOptionsBuilder.fromFnPromise = jasmine.createSpy('DTOptionsBuilder.fromFnPromise');
      mockDTOptionsBuilder.withDataProp = jasmine.createSpy('DTOptionsBuilder.withDataProp');

      mockDTColumnBuilder.newColumn = jasmine.createSpy('DTColumnBuilder.newColumn');

    });

  });

  beforeEach(inject(function ($controller, $rootScope, _$state_, _DTColumnBuilder_, _DTOptionsBuilder_) {
    scope = $rootScope.$new();
    $state = _$state_;
    DTOptionsBuilder = _DTOptionsBuilder_;
    DTColumnBuilder = _DTColumnBuilder_;

    $controller('MarketsCtrl', {
      $scope: scope,
      $state: $state,
      DTOptionsBuilder: DTOptionsBuilder,
      DTColumnBuilder: DTColumnBuilder
    });

    scope.$digest();
  }));


  it('should provide an edit function', function () {
    expect(typeof scope.edit).toBe('function');
  });


});

我认为创建一个模拟并在其上放置一个 Spy 会阻止它调用链式函数,但我想不会。

我对测试很陌生,尤其是使用 Angular,所以任何一般的帮助都将不胜感激!

最佳答案

我从 sinonjs 接近这个/mocha背景

我在解密你的 beforeEach block 时遇到了一些麻烦 - 但它可以在某种程度上简化:

var $scope, $state, DTColumnBuilder, DTOptionsBuilder, createController;

beforeEach(function () {
  DTColumnBuilder  = {};
  DTOptionsBuilder = {};
  $state           = {};

  module('app', function ($provide) {
    $provide.value('$state', $state);
    $provide.value('DTColumnBuilder', DTColumnBuilder);
    $provide.value('DTOptionsBuilder', DTOptionsBuilder);
  });

  inject(function ($controller, $injector) {
    $scope = $injector.get('$rootScope').$new();
    $state = $injector.get('$state');
    DTColumnBuilder  = $injector.get('DTColumnBuilder');
    DTOptionsBuilder = $injector.get('DTOptionsBuilder');

    createController = function () {
      return $controller('MarketsCtrl', {
        $scope: scope,
        $state: $state,
        DTOptionsBuilder: DTOptionsBuilder,
        DTColumnBuilder: DTColumnBuilder        
      });
    }
  });

  // Stub out the methods of interest. 
  DTOptionsBuilder.fromFnPromise = angular.noop;
  $state.go = function () { console.log('I tried to go but.... I cant!!');
  DTColumnBuilder.bar = function () { return 'bar'; };
});
spy 的性质是让原始实现做它的事情,但记录对所述函数的所有调用和各种相关数据。

一个 stub另一方面是 spy使用扩展的 API,您可以在其中完全修改所述功能的工作方式。返回值、预期参数等

假设我们使用了前面提到的 beforeEach block ,DTOptionsBuilder.fromFnPromise将是 noop在此刻。因此,spy 是安全的并期望该方法已被调用。
it('should have been called', function () {
  var spy = spyOn(DTOPtionsBuilder, 'fromFnPromise');
  createController();
  expect(spy).toHaveBeenCalled();
});

如果你想操纵所述函数的返回值,我会捕获 sinonjs并将其设为 stub .
it('became "foo"', function () {
  DTOptionsBuilder.fromFnPromise = sinon.stub().returns('foo');
  createController();
  expect($scope.dtOptions).toEqual('foo');
});

现在,由于您正在使用 Promise,所以它有点复杂,但 stub 基于 Promise 的函数的基础是:
  • 注入(inject) $q进入你的规范文件。
  • 告诉 stub返回 $q.when(/** value **/) 的情况下已解决的 promise 。
  • 告诉 stub返回 $q.reject(/** err **/) 的情况下拒绝 promise 。
  • 运行$timeout.flush()刷新所有延迟任务。
  • 触发done回调以通知 Jasmine 您已完成等待异步任务(可能不需要)。这取决于测试框架/运行器。

  • 它可能看起来像这样:
    it('resolves with "foo"', function (done) {
      DTOptionsBuilder.fromFnPromise = sinon.stub().returns($q.when('foo'));
      expect($scope.options).to.eventually.become('foo').and.notify(done); // this is taken from the chai-as-promised library, I'm not sure what the Jasmine equivalent would be (if there is one).
      createController();
      $timeout.flush();
    });
    

    现在,很多这只是在这一点上的猜测。如果没有源代码在我旁边运行以供交叉引用,很难设置一个完整工作的测试套件,但我希望这至少会给你一些关于如何使用你的 spy 和 stub 的想法。

    关于angularjs - 测试使用数据表的 Angular Controller - 模拟 DTOptionsBuilder 和 DT ColumnBuilder,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31328414/

    相关文章:

    javascript - AngularJS - 过滤器返回引用?

    javascript - 您如何截取 angularjs 应用程序的屏幕截图?

    c# - 使用 LINQ to DataTable 确定外键表是否与主键表有任何链接

    jquery - 为什么我在清除数据表时得到 .clear() is not a function ?

    .net - 100 万行数据的数据清洗 - .Net

    css - 使用 ng-table 时如何编辑排序字形的颜色?

    angularjs - 为什么单击按钮时警报会显示两次?

    c# - 这是使用和测试使用工厂模式的类的正确方法吗?

    使用 robolectric 进行 Android gradle 测试

    visual-studio-2010 - 为什么 ReSharper 的测试运行器会忽略 ExpectedException?