web-dev-qa-db-fra.com

Comment tester le fournisseur personnalisé AngularJS

Quelqu'un a-t-il un exemple de test unitaire d'un fournisseur?

Par exemple:

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 utilise dans les tests et je vois déjà configuré configProvider et je peux le tester en tant que service. Mais comment puis-je tester la possibilité de configurer? Ou il n'a pas besoin du tout?

52
Maxim Grach

J'avais cette même question et je n'ai trouvé qu'une solution de travail dans ce réponse du groupe Google et il est référencé exemple de violon .

Tester votre code fournisseur ressemblerait à quelque chose comme ça (en suivant le code dans le 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');
        });
    });

});
64
Mark Gemmill

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

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

Donc,

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 la méthode $ get de vos fournisseurs a des dépendances, vous pouvez 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 l'injecteur $ 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 options ci-dessus vous permettent également de reconfigurer le fournisseur pour chaque instruction it individuelle dans un bloc describe. Mais si vous n'avez besoin de configurer le fournisseur qu'une seule 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() {
    ...
});
46
james

La réponse de @Stephane Catala a été particulièrement utile et j'ai utilisé son providerGetter pour obtenir exactement ce que je voulais. Il était important de demander au fournisseur de faire l'initialisation, puis le service réel pour valider que les choses fonctionnent correctement avec divers paramètres. 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');
        });

    });
3
ScottG

voici un petit assistant qui encapsule correctement les fournisseurs de récupération, garantissant 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 {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
  }
  • le processus de récupération 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 "inject", qui peuvent être respectivement regroupées avec d'autres appels à "module" et "inject" dans un test unitaire.
  • si le fractionnement n'est pas nécessaire, la récupération d'un fournisseur peut simplement être effectuée en une seule commande!
2
Stephane Catala

Personnellement, j'utilise cette technique pour simuler des fournisseurs provenant de bibliothèques externes, que vous pourriez mettre dans un fichier d'aide pour tous vos tests. Cela peut également 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 = 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
Guillaume

Je n'avais besoin que de tester que certains paramètres étaient correctement définis sur le fournisseur, j'ai donc utilisé Angular DI pour configurer le fournisseur lorsque j'initialisais le module via module().

J'ai également eu quelques problèmes avec le fournisseur non trouvé, après avoir essayé certaines des solutions ci-dessus, ce qui a souligné la nécessité d'une approche alternative.

Après cela, j'ai ajouté d'autres tests qui utilisaient les paramètres pour vérifier qu'ils reflétaient l'utilisation de la 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);
        });

    });

});
1
Ash Clarke