How to test a custom AngularJS provider

Does anyone have an example using a unit test provider?

For example:

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 used in tests, and I see the already configured configProvider , and I can check it as a service. But how can I check the customization option? Or is it not necessary at all?

+50
angularjs unit-testing jasmine
Feb 08 '13 at 11:44
source share
6 answers

I had the same question, and I only found a working solution in this Google Group answer and referred to an example script .

Testing the code of your provider will look something like this (following the code in the example script and what worked for me):

 describe('Test app.config provider', function () { var theConfigProvider; beforeEach(function () { // Initialize the service provider // by injecting it to a fake module 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'); }); }); }); 
+63
Jun 29 '13 at 9:06 on
source share

I use the @Mark Gemmill solution and it works well, but then came across this slightly less detailed solution that eliminates the need for a fake module.

stack overflow

So,

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


If your providers $ get method has dependencies, you can pass them manually,

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


Or use the $ injector to create a new 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'); })); 


Both of the above will also allow you to reconfigure the provider for each individual it statement in the describe block. But if you only need to configure the provider once for several tests, you can do this,

 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() { ... }); 
+44
Oct 31 '14 at 11:54
source share
Answer to

@Stephane Catala was especially helpful, and I used its providerGetter to get exactly what I wanted. The ability to force the provider to perform initialization and then the actual service to verify the correct operation with various settings is important. Code example:

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

Jasmine Test Specification:

  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
Jul 19 '15 at 3:43 on
source share

here is a small helper that correctly encapsulates vendor samples, therefore, provides isolation between individual tests:

  /** * @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 } 
  • the provider extraction process is completely encapsulated: there is no need for closing variables that reduce isolation between tests.
  • the process can be divided into two stages: the “module” step and the “injection” step, which can be grouped accordingly with other calls of the “module” and “injection” in the unit test.
  • If separation is not required, obtaining a provider can be done with only one command!
+2
Apr 22 '15 at 17:52
source share

Personally, I use this technique to bully providers coming from external libraries that you could add to the helper file for all your tests. Of course, it can also work for a custom provider, as in this matter. The idea is to override the provider in your module before it is called by the 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
Apr 21 '16 at 3:38 on
source share

I only needed to check the correctness of the installation of some parameters at the provider, so I used Angular DI to configure the provider when I initialized the module through module() .

I also had some problems with a provider not found, having tried some of the above solutions to emphasize the need for an alternative approach.

After that, I added additional tests that used settings to verify that they reflect the use of the new setting value.

 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
Aug 12 '16 at 16:35
source share



All Articles