Comment tester le fournisseur personnalisé AngularJS

Quelqu’un at-il un exemple de la façon de tester un fournisseur d’unité?

Par exemple:

config.js

angular.module('app.config', []) .provider('config', function () { var config = { mode: 'dissortingbuted', api: 'path/to/api' }; this.mode = function (type) { if (type) { config.isDissortingbutedInstance = type === config.mode; config.isLocalInstance = !config.isDissortingbutedInstance; 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 utilise dans les tests et je vois configProvider déjà configuré et je peux le tester en tant que service. Mais comment puis-je tester la capacité de configuration? Ou il n’a pas besoin du tout?

J’ai eu la même question et j’ai seulement trouvé une solution de travail dans cette réponse de Google Group et c’est un exemple de violon référencé.

Tester votre code fournisseur ressemblerait à ceci (en suivant le code dans l’ exemple de violon et ce qui a fonctionné pour moi):

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

J’ai utilisé la solution de @Mark Gemmill et cela fonctionne bien, mais je suis tombé sur cette solution légèrement moins verbeuse qui supprime le besoin d’un faux module.

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

Alors,

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

Si votre méthode de fournisseurs $ get a des dépendances, vous pouvez soit les transmettre manuellement,

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

Ou utilisez le $ injector pour créer une nouvelle instance,

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

Les deux ci-dessus vous permettraient également de reconfigurer le fournisseur pour chaque instruction individuelle dans un bloc de describe . Mais si vous avez seulement besoin de configurer le fournisseur une fois pour plusieurs tests, vous pouvez le faire,

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

La réponse de @Stephane Catala a été particulièrement utile et j’ai utilisé son fournisseurGetter pour obtenir exactement ce que je voulais. Il était important d’être capable d’initialiser le fournisseur et de vérifier que les choses fonctionnaient correctement avec divers parameters. Exemple de code:

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

Les spécifications du test 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'); }); }); 

Voici une petite aide qui encapsule correctement les fournisseurs d’extraction, sécurisant ainsi l’isolement entre les tests individuels:

  /** * @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 {ssortingng} moduleName * @param {ssortingng} 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 } 
  • le processus d’extraction du fournisseur est entièrement encapsulé: pas besoin de variables de fermeture qui réduisent l’isolement entre les tests.
  • Le processus peut être divisé en deux étapes: une étape “module” et une étape “injection”, qui peuvent être respectivement regroupées avec d’autres appels à “module” et “injection” dans un test unitaire.
  • Si le fractionnement n’est pas nécessaire, la récupération d’un fournisseur peut se faire simplement en une seule commande!

Personnellement, j’utilise cette technique pour simuler des fournisseurs provenant de bibliothèques externes, que vous pouvez placer dans un fichier d’aide pour tous vos tests. Cela peut aussi fonctionner pour un fournisseur personnalisé comme dans cette question bien sûr. L’idée est de redéfinir le fournisseur dans son module avant qu’il ne soit appelé par l’application

 describe('app', function() { beforeEach(module('app.config', function($provide) { $provide.provider('config', function() { var mode = jasminee.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(); })); }); }); 

Je n’avais qu’à tester que certains parameters étaient définis correctement sur le fournisseur, alors j’ai utilisé Angular DI pour configurer le fournisseur lorsque j’initialisais le module via le module() .

Après avoir essayé certaines des solutions ci-dessus, j’ai également rencontré des problèmes avec le fournisseur, ce qui a souligné la nécessité d’une approche alternative.

Après cela, j’ai ajouté d’autres tests qui utilisaient les parameters pour vérifier qu’ils reflétaient l’utilisation d’une nouvelle valeur de paramètre.

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