2013-10-10 6 views
38

Sto prendendo in giro un servizio per un test dell'unità AngularJS. Sto utilizzando il servizio $provide di sostituire il servizio 'reale' con quello preso in giro fuori (un plunker script di questa è disponibile):

describe('My Controller', function() { 

    var $scope; 
    var $provide; 

    beforeEach(angular.mock.module('myApp')); 

    beforeEach(angular.mock.module(function (_$provide_) { 

     $provide = _$provide_; 

    })); 

    beforeEach(angular.mock.inject(function($rootScope, $controller, $q){ 

     var mockMyService = { 
      getAll : function() { 
       var deferred = $q.defer(); 
       deferred.resolve([ 
      { itemText: "Foo" }, 
      { itemText: "Bar" } 
       ]); 

       return deferred.promise; 
      } 
     }; 

     $provide.value('myService', mockMyService); 

     $scope = $rootScope.$new(); 

     $controller('MyCtrl', { $scope: $scope }); 

     $rootScope.$apply(); 

    })); 

    it('Has two items defined', function() { 
     expect($scope.items.length).toEqual(2); 
    }); 
}); 

Questo funziona bene. Tuttavia, non mi piace il fatto che sto usando una funzione angular.mock.module semplicemente per dare un riferimento al servizio $provide che viene poi utilizzato nella funzione angular.mock.inject di seguito. Ma se aggiungo $provide come parametro alla funzione angular.mock.inject direttamente, ottengo un errore "provider sconosciuto".

Mi viene in mente che potrei inserire tutto il codice di simulazione nella funzione angular.mock.module. Ma poi ho un problema simile con il riferimento $q, di cui ho bisogno perché il mio servizio deriso deve restituire una promessa.

In altre parole, se aggiungo un parametro $q alla funzione angular.mock.module, viene visualizzato anche un errore "provider sconosciuto".

C'è un modo per semplificare questo? Ovviamente quello che ho funziona, ma non mi sembra giusto, in qualche modo. Sento che mi manca la comprensione del motivo per cui alcuni provider sono disponibili nelle funzioni inject e altri sono disponibili nelle funzioni module.

risposta

53

Non è possibile utilizzare la funzione $provide all'interno della funzione inject in quanto la precedente registra i provider che devono essere utilizzati da quest'ultima. Date un'occhiata:

describe('...', function() { 
    beforeEach(function() { 
     module(function($provide) { 
      $provide.constant('someValue', 'foobar'); 
     }); 

     inject(function(someValue) { 
      var value = someValue; // will be 'foobar'; 
     }); 
    }); 
}); 

Si può però scrivere il test in questo modo:

describe('...', function() { 
    var serviceMock; 

    beforeEach(function() { 
     serviceMock = { 
      someMethod: function() { ... } 
     }; 

     module(function($provide) { 
      $provide.value('service', serviceMock); 
     }); 

     inject(function(service) { 
      ...       
     }); 
    }); 
}); 

In realtà, non è nemmeno bisogno di implementare il servizio preso in giro prima di iniettarlo con $provide:

beforeEach(function() { 
    serviceMock = {}; 

    module(function($provide) { 
     $provide.value('service', serviceMock); 
    }); 

    inject(function(service) { 
     ...       
    }); 
}); 

it('tests something', function() { 
    // Arrange 
    serviceMock.someMethod = function() { ... } 

    // Act 
    // does something 

    // Assert 
    expect(...).toBe(...); 
}); 

Ecco uno Plunker script che illustra principalmente di quanto sopra.

+0

Questo è molto bello e funziona ... purché non si usi una Promessa. Ecco un plunk che mostra il mio esempio originale: http://plnkr.co/edit/1Gbr1N?p=preview Ed ecco un fork aggiornato con la tecnica che suggerisci: http://plnkr.co/edit/ptAWcb?p = preview Il servizio di simulazione non viene assegnato; sembra che qualcosa stia interferendo con i tempi. – Holf

+0

Mi chiedo perché non riesco a ottenere la libreria '$ q' dalla funzione 'modulo'? Se potessi, avrei tutto ciò di cui ho bisogno per creare il servizio di simulazione lì. – Holf

+7

@Holf Puoi usare una promessa, ma devi prima muovere le cose un po 'prima. Controlla questo aggiornamento [script Plunker] (http://plnkr.co/edit/Fi1SQq). –

11

Questo ha funzionato per me quando ho dovuto avvolgere un servizio che ha usato $q e sembra abbastanza pulito:

var _ServiceToTest_; 
beforeEach(function() { 
    module('module.being.tested'); 
    module(function ($provide) { 
     $provide.factory('ServiceToMock', function ($q, $rootScope) { 
      var service = ...; 
      // use $q et al to heart's content 
      return service; 
     }); 
    }); 
    inject(function (_ServiceToTest_) { 
     ServiceToTest = _ServiceToTest_; 
    }); 
}); 

it('...', function() { /* code using ServiceToTest */ }); 

Il trucco era quello di utilizzare $provide.factory invece di $provide.value.

Problemi correlati