2015-07-09 1 views
9

Я пытаюсь написать тест для углового контроллера, который в основном создает данные, относящиеся к данным с сервера. Я пробовал насмехаться над DTOptionsBuilder и DTColumnBuilder, но это, похоже, не работает. Я получаю ошибку:Тестирование углового контроллера, который использует Datatables - насмешливый DTOptionsBuilder и DT ColumnBuilder

'неопределенные' не является объектом (оценка 'DTOptionsBuilder.fromFnPromise (функция() { возвращение MarketsFactory.getAll();} ) .withDataProp')

Вот код контроллера:

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


}); 

Я думал, что с повторение макета и установка шпиона на него помешали бы ему вызвать цепочки функций, но, я думаю, нет.

Я совершенно новичок в тестировании, особенно с помощью Angular, поэтому любая помощь в целом будет принята с благодарностью!

ответ

0

Я подхожу к этому с sinonjs/mocha фона

У меня возникли некоторые проблемы дешифровки своего beforeEach блока - но она может быть упрощена до некоторой степени:

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 заключается в том, чтобы позволить оригинальной реализации выполнить свою задачу, но запись все вызовы указанной функции и набор связанных данных.

С другой стороны, spy с расширенным API, в котором вы можете полностью изменить работу указанной функции. Возвращаемое значение, ожидаемые параметры и т. Д.

Предполагая, что мы использовали вышеупомянутый перед каждым блоком, 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'); 
}); 

Теперь, так как вы работаете с обещаниями это чуть-чуть сложнее, но основы гася функции посыла на основе было бы:

  • Inject $q в файл спецификации.
  • Сообщите stub о возврате $q.when(/** value **/) в случае решения .
  • Сообщите stub о возврате $q.reject(/** err **/) в случае отклонено обещание.
  • Запустить $timeout.flush(), чтобы очистить все отложенные задачи.
  • Запуск вызова done для уведомления Жасмина о том, что вы готовы ждать асинхронных задач (может и не понадобиться). Это зависит от среды тестирования/бегуна.

Это может выглядеть так:

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

Теперь, много это просто догадка на данный момент. Трудно настроить полностью рабочий набор тестов, не имея исходного кода, который работает рядом со мной для перекрестной ссылки, но я надеюсь, что это, по крайней мере, даст вам некоторые идеи о том, как идти со своими шпионами и заглушками.