2012-11-27 21 views
6

Ho un servizio che accetta molti dei miei altri servizi come dipendenza. Come posso deriderlo per un test unitario?Mocking il mio servizio in un test di unità

myApp.factory('serviceToTest', 
      ['serviceDependency', 
    function(serviceDependency) { 
     return function(args) { 
      return cond(args) ? serviceDependency() : somethingElse(); 
     }; 
    } 
]); 

Nell'esempio di cui sopra, voglio prendere in giro fuori serviceDependency modo che io possa verificare che è stato chiamato. Come lo posso fare?

ho potuto solo effettuare le seguenti operazioni nel test:

describe("Services", function() { 
    describe('serviceToTest', function() { 

     myApp.factory('serviceDependency', function() { 
      var timesCalled = 0; 
      return function() { 
       return timesCalled++; 
      } 
     }); 

     it('should do foo', inject(function(serviceToTest, serviceDependency) { 
      serviceToTest(["foo", "bar", "baz"]); 
      expect(serviceDependency()).to.equal(1); 
     }); 
    }); 
}); 

Questo funziona bene per il test che ha bisogno del finto, ma poi colpisce lo stato di tutte le altre prove che seguono, che è ovviamente un problema.

+0

Nel test come si ottiene l'istanza di serviceToTest? –

+0

@RoyTruelove OP aggiornato –

risposta

11

Se ho capito bene, vuoi testare un servizio che dipende da un altro servizio e prendi in giro una dipendenza per ogni test. Se è così, diciamo che abbiamo un car che ha una dipendenza su un engine:

var app = angular.module('plunker', []) 
    .factory('car', function(engine) { 
    return { 
     drive : function() { 
     return 'Driving: ' + engine.speed(); 
     } 
    } 
    }) 
    .value('engine', { 
    speed : function() { 
     return 'fast'; 
    } 
    }); 

poi si desidera testare una macchina e prendere in giro un motore. Ci sono 2 modi per farlo: o attraverso la definizione di un nuovo modulo in cui potremmo ridefinire un motore:

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

    beforeEach(function(){ 
    testEngine = {}; 
    angular.module('test', ['plunker']).value('engine', testEngine); 
    module('test'); 
    }); 

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

Un lavoro plunk qui: http://plnkr.co/edit/ueXIzk?p=preview

Un po 'più semplice alternativa, l'inoltro sulla natura dinamica di JavaScript :

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) { 
    testEngine.speed = function() { 
     return 'slow'; 
    }; 
    expect(car.drive()).toEqual('Driving: slow'); 
    })); 
}); 

http://plnkr.co/edit/tlHnsJ?p=preview

un'altra alternativa è quella di utilizzare la spia di un Jasmine:

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

http://plnkr.co/edit/K4jczI?p=preview

+0

puoi spiegare come farlo nel caso in cui non possa effettivamente caricare la dipendenza 'motore'. In quel caso non posso testEngine = motore; parte. –

+0

@KamalReddy 2 anni di ritardo, ma per i posteri: basta usare la prima soluzione sopra. Se si definisce un intero oggetto 'testEngine' che sovrascrive la definizione della dipendenza originale, non importa se è possibile caricare 'engine'. –

7

ho avuto lo stesso problema cercando di iniettare un servizio di finto in un altro provider.

Questo post di Pete Bacone Darwin mi ha messo sulla strada giusta: https://groups.google.com/d/msg/angular/TvBknXnjRyQ/xtCDkJyqp6MJ

Ecco un esempio di applicazione:

angular.module('MyApplication', []) 
    .service('MyServiceDependency', [function(){ 
     // Behaviour here. 
    }]) 
    .factory('MyFactory', ['MyServiceDependency', function(MyServiceDependency){ 
     // Behaviour here. 
    }]); 

Vogliamo prendere in giro fuori MyServiceDependency per testare il comportamento di MyFactory così scriviamo i nostri test di questo tipo:

describe(function(){ 

    var MyFactory; 

    beforeEach(function(){ 
     // 1. Include your application module for testing. 
     angular.mock.module('MyApplication'); 

     // 2. Define a new module. 
     // 3. Define a provider with the same name as the one you want to mock (in our case we want to mock 'MyServiceDependency'. 
     angular.module('MyAppMock', []) 
      .service('MyServiceDependency', function(){ 
       // Define you mock behaviour here. 
      }); 

     // 4. Include your new mock module - this will override the providers from your original module. 
     angular.mock.module('MyAppMock'); 

     // 5. Get an instance of the provider you want to test. 
     inject(function($injector){ 
      // `MyFactory` will receive the mocked version of `MyServiceDependency`. 
      MyFactory = $injector.get('MyFactory'); 
     }); 
    }); 

    it('MyFactory does something special', function(){ 
     MyFactory(); // Test your provider. 
    }); 

}); 
+1

Bellissima - dubito che l'avrei capito da solo. IMHO questo è l'approccio più flessibile poiché ora puoi definire un set di mock personalizzati ** al di fuori ** dei test unitari. Quindi è possibile puntare allo stesso mock nei test unitari come si farebbe quando si imposta un ambiente in esecuzione in cui le dipendenze esterne (chiamate ad esempio a $ http) sono disabilitate. – user1821052

Problemi correlati