2011-08-23 11 views
11

JUnitを使用してOSGiでマルチバンドル統合テストを実装する方法を理解しようとしています。JUnitテストのコンテキストでOSGi宣言型サービスを使用する

統合テストでは、バンドルのサブセットをインスタンス化して、そのサブシステム内の機能を自動的に検証します。

私たちはEquinoxを実行しており、ツールチェーンとしてEclipseを使用しています。 EclipseはOSGiフレームワークを起動してバンドルをインスタンス化する "JUnitプラグインとして実行"オプションを提供しています。これは後続のパスだと思いますが、DS参照をテストに挿入する方法は見つけられません。 ServiceTrackerをさまざまなサービスバンドルにアクセスするためのプログラム的手段として使用していますが、それはDSを使用する目的よりも優れていますか?

OSGIを使い始めたばかりなので、マルチバンドルテストを一緒にするパズルの一部が欠落しているだけです。

アイデア?

ありがとう、ジェラード。

* EDIT:

の場合:SOLUTION *

この問題にさらに見た後、私は最終的にJUnitのプラグイン機能を使用して所定の位置にこのMULT-バンドル統合テストを配置する方法を考え出しました動的なサービスインジェクションを動作させるには、通常、DSで作業するときに挿入される依存関係を宣言する必要があるサービス定義ファイルを作成する必要があります。 このファイルは、通常はOSGI-INF/ディレクトリの下にあります。例えばOSGI-INF/service.xml

service.xmlには、この試験のために必要な依存関係を宣言する必要があり、それ自身のサービスを提供していません:

service.xml 
<?xml version="1.0" encoding="UTF-8"?> 
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="MyTest" activate="startup" deactivate="shutdown"> 

    <implementation class="com.test.functionaltest.MyTester"/> 
    <reference name="OtherService" interface="com.product.service.FooService" policy="static" cardinality="1..1" bind="onServiceUp" unbind="onServiceDown"/> 

</scr:component> 

これはonServiceUp宣言されたメソッドを使用してFooServiceへの依存性を注入するDSを指示します。 onServiceDownは、テストの実行後にOSGiシャットダウン・フェーズ中に呼び出されるため、実装する必要があります。

com.test.functionaltest.MyTesterには、典型的なJUnitプラクティスに従って、実行するテストメソッドが含まれています。

ここまでは、すべてが「本」です。しかし、Junitを実行すると、FooServiceへの参照にアクセスするときにNullPointerExceptionがスローされます。その理由は、OSGiフレームワークがJUnitテストランナーコンテキストと競合状態にあり、通常、Junitテストランナーがそのレースに勝利し、必要なサービスへの参照が注入される前にテストを実行するからです。

この状況を解決するには、OSGiランタイムが動作するのを待つためにJunitテストを行う必要があります。この問題は、テストに必要な依存サービスの数に初期化されたCountDownLatchを使用して解決しました。 すべての依存性注入メソッドがカウントダウンされ、それらがすべて完了すると、テストが開始されます。 fooService参照がOSGiのとJUnitの実行コンテキスト間でサービス参照を共有できるように静的である必要が

private static CountDownLatch dependencyLatch = new CountDownLatch(1);// 1 = number of dependencies required  
static FooService fooService = null; 
public void onFooServiceUp(FooService service) { 
    fooService = service; 
    dependencyLatch.countDown(); 
} 

注:コードは、このようになります。 CountDownLatchは、この共有参照を安全に発行するための高レベルの同期メカニズムを提供します。JUnitフレームワークは依存性を注入するためのOSGi DSサービスを待つか、タイムアウト後に失敗

@Before 
public void dependencyCheck() { 
    // Wait for OSGi dependencies 
    try { 
     dependencyLatch.await(10, TimeUnit.SECONDS); 
     // Dependencies fulfilled 
    } catch (InterruptedException ex) { 
     fail("OSGi dependencies unfulfilled"); 
    } 
} 

この方法:

そして、依存性チェックは、テスト実行の前に添加されるべきです。

これを完全に理解するまでにかなりの時間がかかりました。私はそれが将来、仲間のプログラマーに頭を悩ませてくれることを願っています。

答えて

6

* EDIT:

の場合:SOLUTION *

この問題にさらに見た後、私は最終的にJUnitのプラグイン機能を使用して所定の位置にこのMULT-バンドル統合テストを配置する方法を考え出しました動的なサービスインジェクションを動作させるには、通常、DSで作業するときに挿入される依存関係を宣言する必要があるサービス定義ファイルを作成する必要があります。 このファイルは、通常はOSGI-INF/ディレクトリの下にあります。例えばOSGI-INF/service.xml

service.xmlには、この試験のために必要な依存関係を宣言する必要があり、それ自身のサービスを提供していません:

service.xml 
<?xml version="1.0" encoding="UTF-8"?> 
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="MyTest" activate="startup" deactivate="shutdown"> 

    <implementation class="com.test.functionaltest.MyTester"/> 
    <reference name="OtherService" interface="com.product.service.FooService" policy="static" cardinality="1..1" bind="onServiceUp" unbind="onServiceDown"/> 

</scr:component> 

これはonServiceUp宣言されたメソッドを使用してFooServiceへの依存性を注入するDSを指示します。 onServiceDownは、テストの実行後にOSGiシャットダウン・フェーズ中に呼び出されるため、実装する必要があります。

com.test.functionaltest.MyTesterには、典型的なJUnitプラクティスに従って、実行するテストメソッドが含まれています。

ここまでは、すべてが「本」です。しかし、Junitを実行すると、FooServiceへの参照にアクセスするときにNullPointerExceptionがスローされます。その理由は、OSGiフレームワークがJUnitテストランナーコンテキストと競合状態にあり、通常、Junitテストランナーがそのレースに勝利し、必要なサービスへの参照が注入される前にテストを実行するからです。

この状況を解決するには、OSGiランタイムが動作するのを待つためにJunitテストを行う必要があります。この問題は、テストに必要な依存サービスの数に初期化されたCountDownLatchを使用して解決しました。 すべての依存性注入メソッドがカウントダウンされ、それらがすべて完了すると、テストが開始されます。 fooService参照がOSGiのとJUnitの実行コンテキスト間でサービス参照を共有できるように静的である必要が

private static CountDownLatch dependencyLatch = new CountDownLatch(1);// 1 = number of dependencies required  
static FooService fooService = null; 
public void onFooServiceUp(FooService service) { 
    fooService = service; 
    dependencyLatch.countDown(); 
} 

注:コードは、このようになります。 CountDownLatchは、この共有参照を安全に発行するための高レベルの同期メカニズムを提供します。 JUnitフレームワークは依存性を注入するためのOSGi DSサービスを待つか、タイムアウト後に失敗

@Before 
public void dependencyCheck() { 
    // Wait for OSGi dependencies 
    try { 
     dependencyLatch.await(10, TimeUnit.SECONDS); 
     // Dependencies fulfilled 
    } catch (InterruptedException ex) { 
     fail("OSGi dependencies unfulfilled"); 
    } 
} 

この方法:

そして、依存性チェックは、テスト実行の前に添加されるべきです。

これを完全に理解するまでにかなりの時間がかかりました。私はそれが将来、仲間のプログラマーに頭を悩ませてくれることを願っています。

1

あなたが言及しているEclipseツールには慣れていませんが、Apache Slingで統合テストにPax Examを使用することに成功しました。 Mavenに精通している場合は、https://svn.apache.org/repos/asf/sling/trunk/installer/it/pom.xmlのPOMで始めることができます。また、https://github.com/tonit/Learn-PaxExamも良い出発点のようです。

Sling testing toolsは、実行時にバンドルがJUnitテストをOSGiフレームワークに提供できるようにすることで、テストで使用できる実行可能なjarがプロジェクトで生成される場合に役立ちます。

1

実行コンフィギュレーションのタブを使用して設定します。

右クリックして、「実行形式」を選択し、「構成を実行...」を選択し、「JUnitプラグインテスト」をダブルクリックしてから、プラグインタブに依存関係を追加します。通常のランチャー

いくつかのリンク: http://publib.boulder.ibm.com/infocenter/ratdevz/v8r0/index.jsp?topic=/org.eclipse.pde.doc.user/guide/tools/launchers/junit_launcher.htmhttp://publib.boulder.ibm.com/infocenter/ratdevz/v8r0/index.jsp?topic=/org.eclipse.pde.doc.user/guide/tools/launchers/junit_main.htm

+0

これは私が言及した「JUnitプラグインとして実行」オプションですが、宣言型サービスを注入するのには十分ではないようです。つまり、どこにバインディングメソッドを宣言しますか?文脈についての質問もあります。 Junitの状況で注入が行われるのでしょうか?もしそうなら、その仕組みはどのように機能していますか? – maasg

+0

アプリケーションでDSを使用するのと同じです。 DSバンドルを追加する必要があります。これは、ロードされている他のバンドルをスキャンして配線を管理します。 DSバインディングは、バンドルのjar内のOSGI-INFのコンポーネントxmlで宣言されます。 Eclipse PluginプロジェクトでDSを使用する方法については、http://www.vogella.de/articles/OSGi/article.html#declarativeservices_runを参照してください。 HTH – earcam

+0

提案していただきありがとうございます。 DS注入メソッドは、Junit-Pluginの実行のコンテキストで呼び出されることはありません。私のOSG-INFはそれらを正しく宣言します。 – maasg

1

は、私はorg.apache.felix.scr.ScrServiceのホールドを取得し、コンポーネントがアクティブになるのを積極的に待つように少しきれいだろうと思います。このインタフェースは、春分とフェリックスの両方で実装されています。

Java docおよびAPI Usage

-2

私は上記の解決策でCountDownLatchは必要ないと思います。

問題は、DSコンテキスト内のJUnitが、それぞれ自分のためにJUnitTestクラスをインスタンス化することです。最初のDSコンテキストはJUnitTestクラスをインスタンス化し、FooServiceのバインディングonFooServiceUpを呼び出しますが、この後、このJUnitは、バインディングメソッドonFooServiceUpを呼び出さずに、独自のJUnitTestクラスをインスタンス化します。この場合、FooServiceはJUnitTestにありません。

FooServiceをstaticと宣言してonFooServiceUpメソッドに代入する場合、CountDownLatchで構築する必要はありません。

関連する問題