2013-02-08 6 views
50

プロバイダの単体テスト方法の例はありますか?例えば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を構成見て、私はテストすることができそれはサービスとして。しかし、どのように構成する能力をテストできますか?それとも全く必要ないのですか?

答えて

63

私はこの同じ質問をして、このGoogle Group answerの実用的な解決策を見つけただけで、fiddle exampleと表示されています。

fiddle example内のコードとどのような私のために働い以下)、このようなものになり、プロバイダのコードのテスト:私は@マークGemmillのソリューションを使用してきたし、それがうまく動作しますが、

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

}); 
+0

この投稿をいただきありがとうございます。私はここにあるガイドを辿り、失敗しました:https://github.com/angular/angular.js/issues/2274。 上記の例は期待通りに機能しました。ありがとう! – zayquan

+0

これを行うとき、空の関数の代わりに空の配列に渡すように偽のモジュールの宣言を変更しなければなりませんでした。 Angularの新しいバージョンが原因である可能性があります。 –

45

を偽のモジュールの必要性を取り除くこのわずかに冗長な解決策を見つけました。

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


または$を使用して、手動でそれらを渡すことができますいずれか新しいインスタンスを作成するインジェクタ、

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


上記のどちらも、あなたがdescribeブロック内の個々のit文のプロバイダを再構成することができるようになります。あなただけあなたがこれを行うことができ、複数のテストのために一度プロバイダを設定する必要がある場合でも、ここで

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

は、したがって、個々のテストの間のアイソレーションを確保し、適切にフェッチプロバイダーをカプセル化する小さなヘルパーです:

/** 
    * @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つのステップに分けられます。これらのステップは、ユニットテスト内で 'モジュール'と '注入'の他の呼び出しとそれぞれグループ化できます。
  • 分割が必要ない場合は、プロバイダを取得するだけで簡単に1つのコマンドで実行できます。
3

@Stephane Catalaの回答は特に役に立ちました。私は彼のプロバイダGetterを使用して、私が望むものを正確に取得しました。プロバイダがさまざまな設定で正しく動作していることを検証するために、初期化を行い実際のサービスを取得できるようにすることが重要でした。コード例:

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

    }); 
+0

他の人はいつでも自分の答えを削除できます。だから、あなたの答えは他の人には答えてはいけません。 – manetsus

+2

は、クレジットが必要なクレジットを提供しようとしています。私の答えは以前のものがなくても完了しましたが、それはproviderGetterの方が深いですが、プロバイダーフェーズとサービスフェーズの両方をテストする方法が分かりました。私はここでそれを投げるだろうと思った - 他の答えは設定と使用の両方をテストするようではない。 – ScottG

+0

それはいいですが、信用は信用の場所に行くでしょう。この場合、あなたが投票を断念する権利を持っている場合、信用の場所は投票を諦めています。さもなければ、投票を断念する機会を待つ。あなたの答えはよりよいものかもしれませんが、それは私の話題ではありません(あなたの質問を検討するために私がここに関わっているように)、それは他の答えに信用を与える正しい場所ではありません。詳細については、MET SOに参加し、コミュニティ標準について詳しく知ってください。 @ScottG – manetsus

1

個人的に私はすべてのあなたのテストのためのヘルパーファイルに入れることができ、外部のライブラリから来るプロバイダを模擬するために、この技術を使用しています。もちろん、この質問のようにカスタムプロバイダのために働くこともできます。アイデアは、それは私が唯一のいくつかの設定は、プロバイダに正しく設定されていたので、私は時にプロバイダを設定するには、角度DIを使用することをテストするために必要なアプリ

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

によって呼び出される前に、彼のモジュールに、プロバイダを再定義することです私はmodule()経由でモジュールを初期化していました。

上記の解決策のいくつかを試してみても、プロバイダが見つからないという問題もありました。そのため、代替アプローチの必要性が強調されました。

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

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

    }); 

}); 
関連する問題