2013-01-09 4 views
50

Sto testando il servizio A, ma il servizio A dipende dal servizio B (ad esempio, il servizio B viene iniettato nel servizio A).Iniezione di servizi dipendenti durante il test dell'unità Servizi AngularJS

Ho visto this question ma il mio caso è un po 'diverso perché a mio parere è più sensato per finta servizio B invece di iniettare un caso reale di servizio B. avrei rido con una spia di gelsomino.

Ecco un test di esempio:

describe("Sample Test Suite", function() { 

    beforeEach(function() { 

    module('moduleThatContainsServiceA'); 

    inject([ 
     'serviceA', function(service) { 
     this.service = service; 
     } 
    ]); 

    }); 

    it('can create an instance of the service', function() { 
    expect(this.service).toBeDefined(); 
    }); 
}); 

L'errore che ottengo è:

Error: Unknown provider: serviceBProvider

Come potrei fare qualcosa di simile?

+0

FWIW: Ho chiesto a un ** ** versione QUnit di questa domanda [qui in CodeReview.SE] (http://codereview.stackexchange.com/q/98519/10034). – Jeroen

risposta

43

Attualmente in AngularJS Dipendenza Iniezione utilizza la regola "ultime vincite". Quindi puoi definire il tuo servizio nel tuo test subito dopo aver incluso il modulo e le dipendenze, e quando il servizio A che stai testando richiederà il servizio B usando DI, AngularJS fornirà la versione di servizio beffata B.

Questo è spesso viene fatto definendo un nuovo modulo come MyAppMocks, mettendo lì i servizi/valori mocked e aggiungendo semplicemente questo modulo come dipendenza.

Tipologia (schematicamente):

beforeEach(function() { 
    angular.module('MyAppMocks',[]).service('B', ...)); 
    angular.module('Test',['MyApp','MyAppMocks']); 
    ... 
+2

Mi hai appena salvato la vita!: D Mi stavo uccidendo cercando di iniettare un servizio beffato in un altro servizio che dipendeva da questo, ma solo i test avrebbero usato la versione derisoria, non il servizio inserito. Ora ho un modulo Mock separato che carico dopo il modulo dell'app che sovrascrive i servizi ricercati. Funziona come un fascino! –

+3

Thomas, potresti condividere alcuni dettagli sulla tua soluzione? Ho 2 moduli, ognuno dei quali contiene un servizio, e service_1 dal primo modulo è iniettato in service_2 nel secondo modulo. Sto creando un modulo fittizio con service_1 che dovrebbe sovrascrivere il servizio originale_1. Ed è sovrascritto, ma solo nei test, quindi quando chiamo service_2, utilizza ancora il servizio originale_1 all'interno. –

+3

Come gestisco [aggiunta di mock ai servizi] (https://github.com/daniellmb/angular-test-patterns/blob/master/patterns/service.md#suggested-service-unit-test-setup-) – daniellmb

20

La soluzione Valentyn funzionato per me, ma c'è un'altra alternativa.

beforeEach(function() { 

    angular.mock.module("moduleThatContainsServiceA", function ($provide) { 
       $provide.value('B', ...); 
      }); 
}); 

Poi, quando AngularJS servizio A richiedere il servizio B da Dependency Injection, la vostra finta di Servizio B saranno forniti al posto del Servizio B da moduleThatContainsServiceA.

In questo modo non è necessario creare un modulo angolare aggiuntivo solo per simulare un servizio.

+0

Eccellente. Nel mio caso "..." sono stati sostituiti con "{}". Bam, completamente rimosso una dipendenza. – 2mia

+0

Il problema con questo è che ti stai ancora prendendo in giro durante il test. Dì che hai bisogno di deriderlo in diversi punti e qualcosa cambia. Quindi dovresti cambiare ogni file invece di cambiare in un posto. Per ragioni di manutenibilità, la risposta di Valentyns dovrebbe essere accettata (che è). – perry

24

Lo stavo facendo in CoffeeScript e ho trovato un trucchetto extra. (Inoltre, ho trovato il codice in questa pagina per essere confusamente concisa.) Ecco un esempio completo di lavoro:

describe 'serviceA', -> 
    mockServiceB = {} 

    beforeEach module 'myApp' # (or just 'myApp.services') 

    beforeEach -> 
     angular.mock.module ($provide) -> 
     $provide.value 'serviceB', mockServiceB 
     null 

    serviceA = null 
    beforeEach inject ($injector) -> 
     serviceA = $injector.get 'serviceA' 

    it 'should work', -> 
     expect(true).toBe(true) 
     #serviceA.doStuff() 

Senza fare esplicito ritorno nulli dopo $provide.value, ho continuato a ottenere Error: Argument 'fn' is not a function, got Object. Ho trovato la risposta in questo Google Groups thread.

+1

Prova a usare vuoto 'return' invece di' null'. In questo modo il tuo javascript generato non avrà ulteriore riga "return null" alla fine della funzione. Invece, la funzione 'beforeEach' non restituirà nulla. – demisx

+0

Sto cercando di utilizzare un codice simile al momento, ma ottengo un "SyntaxError: Unexpecte string" serviceA'' se dovessi usare il tuo codice. Qualche idea su come risolvere questo? - Anche usando Coffeescript –

+0

@MathieuBrouwers, probabilmente dovresti aprire una nuova domanda. Non sono sicuro di cosa ti riferisci esattamente, e dovrei vedere il tuo codice per rispondere. – jab

1

Questo è ciò che ha funzionato per me. La chiave sta definendo un vero modulo da prendere in giro. Chiamare angular.mock.module rende il modulo reale mockable e consente di collegare le cose.

beforeEach(-> 
     @weather_service_url = '/weather_service_url' 
     @weather_provider_url = '/weather_provider_url' 
     @weather_provider_image = "test.jpeg" 
     @http_ret = 'http_works' 
     module = angular.module('mockModule',[]) 
     module.value('weather_service_url', @weather_service_url) 
     module.value('weather_provider_url', @weather_provider_url) 
     module.value('weather_provider_image', @weather_provider_image) 
     module.service('weather_bug_service', services.WeatherBugService) 

     angular.mock.module('mockModule') 

     inject(($httpBackend,weather_bug_service) => 
      @$httpBackend = $httpBackend 
      @$httpBackend.when('GET', @weather_service_url).respond(@http_ret) 
      @subject = weather_bug_service 
     ) 
    ) 
6

Trovo che il metodo più semplice sia solo quello di iniettare il servizio B e deriderlo. per esempio. La macchina di servizio dipende dal motore di servizio. Ora abbiamo bisogno di motore finto durante la prova auto:

describe('Testing a car', function() { 
     var testEngine; 

    beforeEach(module('plunker')); 
    beforeEach(inject(function(engine){ 
    testEngine = engine; 
    })); 

    it('should drive slow with a slow engine', inject(function(car) { 
    spyOn(testEngine, 'speed').andReturn('slow'); 
    expect(car.drive()).toEqual('Driving: slow'); 
    })); 
}); 

Riferimento: https://github.com/angular/angular.js/issues/1635