How to test AngularJS custom provider

I had this same question and only found a working solution in this Google Group answer and it's referenced fiddle example.

Testing your provider code would look something like this (following the code in the fiddle example 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'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');
        });
    });

});

i've been using @Mark Gemmill's solution and it works well, but then stumbled across this slightly less verbose solution which removes the need for a fake module.

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

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 either pass them in 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 would also allow you to reconfigure the provider for each individual it statement in a describe block. But if you only need to configure the provider once for multiple 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() {
    ...
});

@Stephane Catala's answer was particularly helpful, and I used his providerGetter to get exactly what I wanted. Being able to get the provider to do initialization and then the actual service to validate that things are working correctly with various settings was important. Example 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;} };
        }
    }

The Jasmine test spec:

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

    });

here is a little helper that properly encapsulates fetching providers, hence securing 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 process of fetching the provider is fully encapsulated: no need for closure variables that reduce isolation between tests.
  • the process can be split in two steps, a 'module' step and an 'inject' step, which can be respectively grouped with other calls to 'module' and 'inject' within a unit test.
  • if splitting is not required, retrieving a provider can simply be done in a single command!