web-dev-qa-db-ja.com

AngularJSカスタムプロバイダーをテストする方法

プロバイダを単体テストする方法の例はありますか?

例えば:

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はテストで使用されており、すでに構成されているconfigProviderが表示され、サービスとしてテストできます。しかし、どのように構成する機能をテストできますか?それともまったく必要ありませんか?

52
Maxim Grach

私はこれと同じ質問を持っていて、これで有効なソリューションを見つけました Google Group answer そしてそれは 中央の例 を参照しています。

プロバイダーコードのテストは、次のようになります( 中央の例 のコードに従って、何が機能したかを確認します)。

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

私は@Mark Gemmillのソリューションを使用してきましたが、それはうまく機能しますが、偽のモジュールの必要性を排除するこの少し冗長なソリューションに出くわしました。

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

そう、

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


プロバイダーの$ getメソッドに依存関係がある場合、それらを手動で渡すか、

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


または$ injectorを使用して新しいインスタンスを作成し、

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


上記のどちらでも、itブロック内の個々のdescribeステートメントごとにプロバイダーを再構成できます。ただし、複数のテストに対して一度だけプロバイダーを構成する必要がある場合は、これを行うことができます。

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

@Stephane Catalaの答えは特に役に立ちました。私は彼のproviderGetterを使用して正確に欲しいものを取得しました。プロバイダーに初期化を実行させ、実際のサービスでさまざまな設定で物事が正しく機能していることを検証できるようにすることが重要でした。サンプルコード:

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

ジャスミンのテスト仕様:

    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

ここに、フェッチプロバイダーを適切にカプセル化する小さなヘルパーがあります。したがって、個々のテスト間の分離を確保します。

  /**
   * @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
  }
  • プロバイダーを取得するプロセスは完全にカプセル化されています。テスト間の分離を減らすクロージャー変数は不要です。
  • プロセスは、「モジュール」ステップと「注入」ステップの2つのステップに分割できます。これらは、ユニットテスト内で「モジュール」と「注入」の他の呼び出しとそれぞれグループ化できます。
  • 分割が必要ない場合は、単一のコマンドでプロバイダーを取得できます!
2
Stephane Catala

個人的には、この手法を使用して、外部ライブラリから提供されるプロバイダーをモックします。これは、すべてのテストのヘルパーファイルに含めることができます。もちろん、この質問のように、カスタムプロバイダーでも機能します。アイデアは、アプリによって呼び出される前に、モジュール内のプロバイダーを再定義することです

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

プロバイダーでいくつかの設定が正しく設定されていることをテストするだけでよいため、module()を介してモジュールを初期化するときにプロバイダーを構成するためにAngular DIを使用しました。

上記のソリューションのいくつかを試した後、プロバイダーが見つからないという問題もありました。そのため、代替アプローチの必要性を強調しました。

その後、設定を使用して、新しい設定値の使用を反映していることを確認するテストを追加しました。

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