2013-02-08 19 views
50

Qualcuno ha un esempio di come testare un fornitore?Come testare il fornitore personalizzato di AngularJS

Ad esempio:

config.js

angular.module('app.config', []) 
    .provider('config', function() { 
    var config = { 
      mode: 'distributed', 
      api: 'path/to/api' 
     }; 

    this.mode = function (type) { 
     if (type) { 
     config.isDistributedInstance = type === config.mode; 
     config.isLocalInstance = !config.isDistributedInstance; 
     config.mode = type; 
     return this; 
     } else { 
     return config.mode; 
     } 
    }; 

    this.$get = function() { 
     return config; 
    }; 
    }]); 

app.js

angular.module('app', ['app.config']) 
    .config(['configProvider', function (configProvider) { 
    configProvider.mode('local'); 
    }]); 

app.js è l'uso di test e vedo già configurato configProvider e posso testare è come un servizio. Ma come posso testare la possibilità di configurare? O non ne ha affatto bisogno?

risposta

63

Ho avuto questa stessa domanda e ho trovato solo una soluzione funzionante in questo Google Group answer e viene referenziato fiddle example.

Testare il codice fornitore sarebbe simile a questa (seguendo il codice nella fiddle example e che cosa ha funzionato per me):

describe('Test app.config provider', function() { 

    var theConfigProvider; 

    beforeEach(function() { 
     // Initialize the service provider 
     // by injecting it to a fake module's config block 
     var fakeModule = angular.module('test.app.config', function() {}); 
     fakeModule.config(function (configProvider) { 
      theConfigProvider = configProvider; 
     }); 
     // Initialize test.app injector 
     module('app.config', 'test.app.config'); 

     // Kickstart the injectors previously registered 
     // with calls to angular.mock.module 
     inject(function() {}); 
    }); 

    describe('with custom configuration', function() { 
     it('tests the providers internal function', function() { 
      // check sanity 
      expect(theConfigProvider).not.toBeUndefined(); 
      // configure the provider 
      theConfigProvider.mode('local'); 
      // test an instance of the provider for 
      // the custom configuration changes 
      expect(theConfigProvider.$get().mode).toBe('local'); 
     }); 
    }); 

}); 
+0

grazie per questo post! Ho seguito la guida trovata qui e ho fallito: https://github.com/angular/angular.js/issues/2274. L'esempio precedente ha funzionato come previsto. Grazie! – zayquan

+0

In questo caso, ho dovuto modificare la dichiarazione del modulo falso per passare in una matrice vuota invece di una funzione vuota. Probabilmente a causa di una versione più recente di Angular. –

45

Sto usando la soluzione di @ Marco Gemmill e funziona bene, ma poi inciampò in questa soluzione un po 'meno verbosa che rimuove la necessità di un modulo falso.

https://stackoverflow.com/a/15828369/1798234

Quindi,

var provider; 

beforeEach(module('app.config', function(theConfigProvider) { 
    provider = theConfigProvider; 
})) 

it('tests the providers internal function', inject(function() { 
    provider.mode('local') 
    expect(provider.$get().mode).toBe('local'); 
})); 


Se il vostro provider di $ metodo get ha dipendenze, è possibile passarli manualmente,

var provider; 

beforeEach(module('app.config', function(theConfigProvider) { 
    provider = theConfigProvider; 
})) 

it('tests the providers internal function', inject(function(dependency1, dependency2) { 
    provider.mode('local') 
    expect(provider.$get(dependency1, dependency2).mode).toBe('local'); 
})); 


Oppure utilizzare il $ iniettore per creare una nuova istanza,

var provider; 

beforeEach(module('app.config', function(theConfigProvider) { 
    provider = theConfigProvider; 
})) 

it('tests the providers internal function', inject(function($injector) { 
    provider.mode('local') 
    var service = $injector.invoke(provider); 
    expect(service.mode).toBe('local'); 
})); 


Entrambe le risposte consentirebbe anche di riconfigurare il provider per ogni singolo it istruzione in un blocco describe. Ma se avete solo bisogno di configurare il provider una volta per le prove multiple, si può fare questo,

var service; 

beforeEach(module('app.config', function(theConfigProvider) { 
    var provider = theConfigProvider; 
    provider.mode('local'); 
})) 

beforeEach(inject(function(theConfig){ 
    service = theConfig; 
})); 

it('tests the providers internal function', function() { 
    expect(service.mode).toBe('local'); 
}); 

it('tests something else on service', function() { 
    ... 
}); 
2

ecco un piccolo aiutante che incapsula adeguatamente i fornitori di recupero, quindi, garantire l'isolamento tra i singoli test:

/** 
    * @description request a provider by name. 
    * IMPORTANT NOTE: 
    * 1) this function must be called before any calls to 'inject', 
    * because it itself calls 'module'. 
    * 2) the returned function must be called after any calls to 'module', 
    * because it itself calls 'inject'. 
    * @param {string} moduleName 
    * @param {string} providerName 
    * @returns {function} that returns the requested provider by calling 'inject' 
    * usage examples: 
    it('fetches a Provider in a "module" step and an "inject" step', 
     function() { 
     // 'module' step, no calls to 'inject' before this 
     var getProvider = 
     providerGetter('module.containing.provider', 'RequestedProvider'); 
     // 'inject' step, no calls to 'module' after this 
     var requestedProvider = getProvider(); 
     // done! 
     expect(requestedProvider.$get).toBeDefined(); 
    }); 
    * 
    it('also fetches a Provider in a single step', function() { 
     var requestedProvider = 
     providerGetter('module.containing.provider', 'RequestedProvider')(); 

     expect(requestedProvider.$get).toBeDefined(); 
    }); 
    */ 
    function providerGetter(moduleName, providerName) { 
    var provider; 
    module(moduleName, 
      [providerName, function(Provider) { provider = Provider; }]); 
    return function() { inject(); return provider; }; // inject calls the above 
    } 
  • il processo di recupero del provider è completamente incapsulato: nessuna necessità di variabili di chiusura che riducano l'isolamento tra i test.
  • il processo può essere suddiviso in due passaggi, un passaggio di "modulo" e un passaggio di "iniezione", che possono essere raggruppati rispettivamente con altre chiamate a "modulo" e "inietti" all'interno di un test di unità.
  • se la suddivisione non è richiesta, il recupero di un provider può essere eseguito semplicemente con un singolo comando!
3

@Stephane La risposta di Catala è stata particolarmente utile, e ho usato il suo provider Getter per ottenere esattamente quello che volevo. Essere in grado di ottenere il provider per l'inizializzazione e quindi il servizio effettivo per verificare che le cose funzionino correttamente con varie impostazioni era importante. Esempio di codice:

angular 
     .module('test', []) 
     .provider('info', info); 

    function info() { 
     var nfo = 'nothing'; 
     this.setInfo = function setInfo(s) { nfo = s; }; 
     this.$get = Info; 

     function Info() { 
      return { getInfo: function() {return nfo;} }; 
     } 
    } 

Il test Spec Jasmine:

describe("provider test", function() { 

     var infoProvider, info; 

     function providerGetter(moduleName, providerName) { 
      var provider; 
      module(moduleName, 
         [providerName, function(Provider) { provider = Provider; }]); 
      return function() { inject(); return provider; }; // inject calls the above 
     } 

     beforeEach(function() { 
      infoProvider = providerGetter('test', 'infoProvider')(); 
     }); 

     it('should return nothing if not set', function() { 
      inject(function(_info_) { info = _info_; }); 
      expect(info.getInfo()).toEqual('nothing'); 
     }); 

     it('should return the info that was set', function() { 
      infoProvider.setInfo('something'); 
      inject(function(_info_) { info = _info_; }); 
      expect(info.getInfo()).toEqual('something'); 
     }); 

    }); 
+0

Gli altri possono cancellare la loro risposta in qualsiasi momento. Quindi la tua risposta non dovrebbe essere menzionata a nessuna altra risposta. – manetsus

+2

solo cercando di dare credito dove il credito è dovuto. La mia risposta è completa senza la precedente, sebbene approfondisca il provider Getter - ma mi ci è voluto un po 'per capire come testare sia la fase del fornitore che la fase di servizio (come era importante per la mia situazione reale) quindi ho pensato di buttarlo qui - nessuna altra risposta sembra testare sia l'impostazione che l'utilizzo. – ScottG

+0

Va bene, ma il credito andrebbe nel luogo del credito. In questo caso, il luogo del credito si arrende se hai il diritto di rinunciare al voto. altrimenti, aspettate le opportunità per rinunciare al voto. La tua risposta può essere la migliore, ma non è il mio argomento (dato che sono coinvolto qui per rivedere la tua domanda) e non è il posto giusto per dare credito ad altre risposte. Per maggiori informazioni, iscriviti a meta SO e conosci meglio lo standard della community. @ScottG – manetsus

1

Personalmente io uso questa tecnica per deridere i fornitori provenienti da librerie esterne, che si potrebbe mettere in un file di aiuto per tutti i test. Può funzionare anche per un fornitore personalizzato come in questa domanda, naturalmente. L'idea è quella di ridefinire il provider nel suo modulo prima di essere chiamato da app

describe('app', function() { 
    beforeEach(module('app.config', function($provide) { 
    $provide.provider('config', function() { 
     var mode = jasmine.createSpy('config.mode'); 

     this.mode = mode; 

     this.$get = function() { 
     return { 
      mode: mode 
     }; 
     }; 
    }); 
    })); 

    beforeEach(module('app')); 

    describe('.config', function() { 
    it('should call config.mode', inject(function(config) { 
     expect(config.mode).toHaveBeenCalled(); 
    })); 
    }); 
}); 
1

Ho solo bisogno di verificare che alcune impostazioni venivano impostate correttamente dal provider, così ho usato angolare DI configurare il provider quando Stavo inizializzando il modulo tramite module().

Avevo anche alcuni problemi con il provider non trovato, dopo aver provato alcune delle soluzioni di cui sopra, in modo che ha sottolineato la necessità di un approccio alternativo.

Successivamente, ho aggiunto ulteriori test che utilizzavano le impostazioni per verificare che riflettessero l'uso del nuovo valore di impostazione.

describe("Service: My Service Provider", function() { 
    var myService, 
     DEFAULT_SETTING = 100, 
     NEW_DEFAULT_SETTING = 500; 

    beforeEach(function() { 

     function configurationFn(myServiceProvider) { 
      /* In this case, `myServiceProvider.defaultSetting` is an ES5 
      * property with only a getter. I have functions to explicitly 
      * set the property values. 
      */ 
      expect(myServiceProvider.defaultSetting).to.equal(DEFAULT_SETTING); 

      myServiceProvider.setDefaultSetting(NEW_DEFAULT_SETTING); 

      expect(myServiceProvider.defaultSetting).to.equal(NEW_DEFAULT_SETTING); 
     } 

     module("app", [ 
      "app.MyServiceProvider", 
      configurationFn 
     ]); 

     function injectionFn(_myService) { 
      myService = _myService; 
     } 

     inject(["app.MyService", injectionFn]); 
    }); 

    describe("#getMyDefaultSetting", function() { 

     it("should test the new setting", function() { 
      var result = myService.getMyDefaultSetting(); 

      expect(result).to.equal(NEW_DEFAULT_SETTING); 
     }); 

    }); 

}); 
Problemi correlati