2015-07-09 11 views
9

Sto provando a scrivere un test per un controller Angolare che crea principalmente un datatable di valori dal server. Ho provato a deridere DTOptionsBuilder e DTColumnBuilder, ma questo non sembra funzionare. Ottengo l'errore:Test di un controller angolare che utilizza Datatables - mocking DTOptionsBuilder e DT ColumnBuilder

'undefined' non è un oggetto (la valutazione 'DTOptionsBuilder.fromFnPromise (function() { ritorno MarketsFactory.getAll();} ) .withDataProp')

Ecco la controller cod:

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

E il file di test:

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


}); 

ho pensato che c battere una finta e mettere una spia su di essa gli impedirebbe di chiamare le funzioni concatenate, ma immagino di no.

Sono abbastanza nuovo per i test, specialmente con Angular, quindi qualsiasi aiuto in generale sarebbe molto apprezzato!

risposta

0

sto avvicinando questo da un sinonjs/mocha sfondo

sto avendo qualche difficoltà a decifrare il tuo blocco beforeeach - ma può essere semplificata in una certa misura:

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

La natura di un spy è quella di lasciare che l'implementazione originale faccia il suo dovere, ma registrazione tutte le chiamate a detta funzione e un assortimento di dati associati.

A stub d'altra parte è un spy con un'API estesa in cui è possibile modificare completamente il funzionamento di detta funzione. Valore di ritorno, parametri attesi, ecc.

Supponendo di aver utilizzato il suddetto blocco Prima di ogni, DTOptionsBuilder.fromFnPromise sarebbe un noop a questo punto. In quanto tale, sarebbe sicuro su spy e aspettarsi che il metodo sia stato chiamato.

it('should have been called', function() { 
    var spy = spyOn(DTOPtionsBuilder, 'fromFnPromise'); 
    createController(); 
    expect(spy).toHaveBeenCalled(); 
}); 

Se si voleva manipolare il valore di ritorno di detta funzione, vorrei afferrare sinonjs e renderlo un stub.

it('became "foo"', function() { 
    DTOptionsBuilder.fromFnPromise = sinon.stub().returns('foo'); 
    createController(); 
    expect($scope.dtOptions).toEqual('foo'); 
}); 

Ora, dal momento che si sta lavorando con la promessa che è un pochino più complicato, ma i principi fondamentali di spegnendo una funzione promessa base sarebbe quella di:

  • Iniettare $q nel file spec.
  • Comunicare al stub di restituire $q.when(/** value **/) in caso di una promessa risolta di .
  • Dì al stub di restituire $q.reject(/** err **/) nel caso di una promessa rifiutata .
  • Eseguire $timeout.flush() per svuotare tutte le attività posticipate.
  • Attivare il callback done per notificare a Jasmine che si è in attesa di attività asincrone (potrebbe non essere necessario). Questo dipende dal framework di test/runner.

E potrebbe guardare qualcosa in questo modo:

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

Ora, un sacco di questo è solo congetture, a questo punto. È abbastanza difficile impostare una suite di test pienamente funzionante senza avere il codice sorgente in esecuzione accanto a me per riferimenti incrociati, ma spero che questo ti fornisca almeno alcune idee su come andare avanti con le tue spie e gli stub.

Problemi correlati