Pax Examの使い方

Pax Constructの次はPax Exam、ということで試してみました。今回はを参照しながらやってみました。

Pax Examの目的

Pax ExamはOSGi環境における試験用の環境を構築してくれるものです。*1例えばPax Examを用いてベースとなるOSGiコンテナを切り替えてもBundleが読み込まれるかとか、Bundle間連携がうまくいくかとか、そういうテストを行います。個々のBundleに閉じたテストも行うことはできますが、それは通常のJUnitを使ったテストで十分です。なぜなら、個々のBundle単体でみた場合、JARファイルと大きな違いはないからです。Pax Examは、開発されたBundleを統合して、システムテストを行う環境として開発されたのでしょう。それでは手順をみていきましょう。

Pax Exam環境を構築する

まずPax Exam環境を構築しましょう。Pax Exam環境は、Bundleを統合するテストを行う環境なので、個々のBundleとは違うBundleを作成した方がわかりやすいです。またPax Examのテストクラスを開発するBundleは、Pax Constructを使用して、適当なPax Exam用のBundleを作成し、に記述されているpax-exam、pax-exam-junit、pax-exam-container-defaultの3つをpom.xmlに追記しました。

テストを書いてみる

それではPax Examを使ったテストを書いてみます。Pax Examで作成するテストクラスは、実際にアプリケーションを構成するBundleとは別のプロジェクトにした方がいいです。通常のJUnit4Runnerではなく、PaxRunnerで用意されたJUnit4TestRunnerを@RunWithで指定します。見ていくと分かるんですが、起動するOSGiフレームワークの指定なんかも、このテストクラスの中で書きます。テストで必要なBundleも、テストクラスの中で指定します。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;

@RunWith( JUnit4TestRunner.class )
public class MyJUnitTest{
  @Test
  public void testMethod(){

  }
}

Bundle単体のテストは、OSGiコンテナ上に載せてから行うんじゃなく、通常のJUnitのテストで行うと割り切っているんでしょう。Bundle単体で考えた場合、クラスローダ環境は、そのBundle単体とOSGi環境と変わりない、として考えられているんじゃないっすかね。this.getClass().getResouce(String)で読む限り、リソースの読み方も違いないですから。OSGi環境に置ける大事な考え方なんですが、基本的に他のBundleの中身を読むことはできません。Bundleが変われば違う世界。なので、これまでのアプリケーションのように、利用しているライブラリの中身を読む、みたいな事はライブラリ役のBundleが許可していない限りできないので、これでテスト環境としては十分です。それではテストを実行してみてください。コンソール上にfelixが立ち上がり、何事もなく終わってくれたでしょうか?

起動したOSGiコンテナのBundleContextをテストクラスにInject

OSGiコンテナのBundleContextを使ったテストをやるにはこんな感じで@Injectを指定すればおkです。

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.osgi.framework.BundleContext;
import org.ops4j.pax.exam.Inject;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;

@RunWith( JUnit4TestRunner.class )
public class MyJUnitTest{
  @Inject
  private BundleContext bundleContext;

  @Test
  public void testMethod(){
    assertThat( bundleContext, is( notNullValue() ) );
  }
}

BundleContextなんてテスト中に何をするんだろう、と思ったあなた。このテストケースはあくまでOSGiコンテナ上での統合テストです。いや、BundleContextなんていいから、直接BundleContextに登録されているサービスをインジェクトしろよ、と思ったあなた。この段階では、実際はなにもBundleが登録ていないので、なんもサービスはありません。実行してみてください。

指定したBundleをOSGiコンテナにインストールしてテストする

OSGiコンテナにBundleをインストールすることをPax Examではprovisionと呼んでいます。サンプルのテストコードをみると分かるんですが、Bundle化されたJARファイルをURLで指定してます。*2

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.ops4j.pax.exam.CoreOptions.*;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;

@RunWith( JUnit4TestRunner.class )
public class MyJUnitTest{

  @Configuration
  public static Option[] configuration(){
    return options(
      provision(
        bundle( "http://repository.ops4j.org/maven2/org/ops4j/pax/logging/pax-logging-api/1.3.0/pax-logging-api-1.3.0.jar" ),
        bundle( "http://repository.ops4j.org/maven2/org/ops4j/pax/logging/pax-logging-service/1.3.0/pax-logging-service-1.3.0.jar" ),
        mavenBundle().groupId( "org.ops4j.pax.url" ).artifactId( "pax-url-mvn" ).version( "0.3.2" )
      )
    );
  }

  @Test
  public void testMethod() throws Exception{
    assertThat( new URL( "mvn:org.ops4j.pax.web/pax-web-service" ).openStream(), is( notNullValue() ) );
  }
}

開発中のBundleもmavenBundle()で指定出来ます。Configurationで指定されているprovision()等のメソッドを提供しているのは「import static org.ops4j.pax.exam.CoreOptions.*;」なので、つけるのを忘れないでください。

じゃ、Felix以外のコンテナに載っけてみっか。

Felix以外のコンテナに載せるには下記のように指定します。

  @Configuration
  public static Option[] configuration()
  {
    return options(
      frameworks(
        felix(),
        equinox(),
        knopflerfish()
      )
    );
  }

この例ではFelixでテストを行った後、equinoxを起動し、さらにknopflerfishでもテストを行います。もちろん単体でも実行可能です。この@Configurationではシステムプロパティとか、ブート時のパッケージ等も指定できます。ブート時のパッケージ指定は、組み込みの用途でしょう。Webアプリケーションとか、PC用のアプリケーションでは使うことはないと思います。お伝えしたかったのは、@Configurationには大抵の環境のシミュレーションができるということ。詳しくはをみてみませう。

*1:Eclipseのプラグイン開発環境のJUnit Plug-in Testとは意味が異なります。JUnit Plug-in TestはあくまでEclipse環境上で動かすプラグインのテストを行うための環境が提供されます。なので、起動時にEclipseのproductかapplicationを指定する必要がありますが、Pax ExamはOSGiコンテナを切り替えたテストも行えます。開発しているBundleが他のOSGiコンテナで実行できるか、検証するための環境でした。

*2:なんも知らないとビビりますが、元々JARはそういうもんです。JARに署名をつけられるのは、署名をつけた人が作ったものである事を保証するためですよね。