S2JUnit4
S2JUnit4はJUnit4を拡張したテスティングフレームワークです。 S2JUnit4はアノテーションと命名規則をバランス良く組み合わせ少ないコードで効率のよいテストを可能にします。
S2JUnit4の主な機能には次のものがあります。
- 命名規則に従い初期化メソッドやテストメソッドを適切な順序で呼び出します。JUnit4のアノテーションと併用することも可能です。
- テストメソッド実行のための事前条件を指定できます。
- テストメソッド実行時のトランザクションの振る舞いを指定できます。
- 複数の設定ファイルを自動でインクルードできます。
- テストごとに異なる事前データをテスト実行前に準備することができます。
- テストごとに異なるテストの期待値を簡単に取得できます。
- テストメソッド実行直前にコンポーネントを自動でテストクラスのフィールドにセットできます。
- BeanやEntityを期待値として用意されるDataSetと比較するためのアサートメソッドを提供します。
- モックを利用したテストをサポートします。
- JUnit4.4が必要です。
- S2-Tigerが必要です。
- S2JUnit4を起動するにはs2junit4.diconがCLASSPATHに含まれている必要があります。
S2Unitのサンプルと同様、従業員を従業員番号で検索するDAOをサンプルとします。設定ファイルとテストクラスはそれぞれ以下のようになります。
EmployeeDaoImplTest.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components>
<include path="javaee5.dicon"/>
<component class="examples.s2junit4.EmployeeDaoImpl">
<property name="getEmployeeHandler">
<component class="org.seasar.extension.jdbc.impl.BasicSelectHandler">
<property name="sql">
"SELECT e.empno, e.ename, e.deptno, d.dname FROM emp e, dept d
WHERE e.empno = ? AND e.deptno = d.deptno"
</property>
<property name="resultSetHandler">
<component class="org.seasar.extension.jdbc.impl.BeanResultSetHandler">
<arg>@examples.s2junit4.Employee@class</arg>
</component>
</property>
</component>
</property>
</component>
</components>
examples.s2junit4.EmployeeDaoImplTest
package examples.s2junit4;
import static org.seasar.framework.unit.S2Assert.assertEquals;
import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.seasar.framework.unit.Seasar2;
import org.seasar.framework.unit.TestContext;
@RunWith(Seasar2.class)
public class EmployeeDaoImplTest {
private TestContext ctx;
private EmployeeDao dao;
public void getEmployee() throws Exception {
Employee emp = dao.getEmployee(9900);
assertEquals("1", ctx.getExpected(), emp);
}
@Ignore("not implemented.")
public void getEmployeeByName() throws Exception {
}
}
このテストクラスのポイントを説明します。
- クラスに
@RunWith(Seasar2.class) と指定します。これはこのテストクラスをS2JUnit4で動作させるために必要です。
- TestContextインタフェース型の変数をフィールドに宣言します。自動フィールドバインディングにより変数ctxにはテストコンテキストが自動でセットされます。このサンプルではテストコンテキストの
getExpected() メソッドを使用し期待値を取得しています。
- テスト対象のコンポーネントのインタフェースを型とする変数を宣言します。自動フィールドバインディングにより変数daoにはコンポーネントが自動でセットされます。ここではEmployeeDaoが相当します。
- S2Assertをstaticインポートします。
assertEquals メソッドはテストメソッドgetEmployee() 内でDataSetとBeanを比較するために使用します。
- テストメソッド
getEmployee() を用意します。テストメソッドはpublic 、非static 、引数なし、戻り値なしでなければいけません。テストメソッド実行前にはトランザクションが自動で開始され、実行後にはトランザクションが自動でロールバックされます。
- 実行したくないテストメソッドには
@Ignore を指定します。これはJUnit4のアノテーションです。S2JUnit4ではJUnit4のアノテーションをそのまま利用できます。
このテストクラスはs2junit4.diconの記述に従い、設定ファイル、準備値、期待値を自動で読み込みます。
- デフォルトの設定ではjavaee5.diconとテストクラスと同一パッケージにありテストクラスと同じ名前をもつdiconファイルが自動でインクルードされます。したがってこのサンプルではjavaee5.diconとEmployeeDaoImplTest.diconの2つの設定ファイルがインクルードされます。
- テストクラスと同一パッケージにあり、"修飾なしテストクラス名_テストメソッド名.xls"もしくは"修飾なしテストクラス名.xls"という名前のExcelが存在すれば、自動で読み込まれ事前データとしてデータベースに追加されます。このサンプルではEmployeeDaoImplTest_
getEmployee.xlsが読み込まれます。自動でトランザクションがロールバックされるのでデータベースのデータはテスト終了後にはテスト開始前の状態に戻ります。
- TestContextインタフェースのgetExpected()メソッドを実行すると、テストクラスと同一パッケージにある”テストクラス名_テストメソッド名_Expected.xls”という名前のExcelが期待値として自動で読み込まれます。このサンプルではEmployeeDaoImplTest_ getEmployee_Expected.xlsが読み込まれます。
準備値と期待値のExcelを用意する方法はS2Unitのサンプルを参考にしてください。
S2JUnit4は命名規則により初期化メソッドやテストメソッドを判別します。また、JUnit4のアノテーションと併用することも可能です。以下は命名規則の一覧です。対応するアノテーションがJUnit4に存在する場合はそのアノテーションを示しています。メソッドのシグネチャはすべてpublic 、引数なし、戻り値なしでなければいけません。さらに、テストクラスの初期化/解放メソッドはstatic 、それ以外のメソッドは非static でなければいけません。 JUnit4のアノテーションとS2JUnit4の命名規則が1つのメソッドに対して同時に使用される場合、命名規則よりもアノテーションが優先されます。命名規則はカスタマイズできます。カスタマイズの方法はS2JUnit4のカスタマイズを参照ください。
メソッドの種類 |
命名規則 |
対応するJUnit4のアノテーション |
説明 |
テストクラスの初期化メソッド |
beforeClass |
@BeforeClass |
ひとつのクラスに@BeforeClass が指定されたメソッドとbeforeClass() メソッドが混在する場合、beforeClass() メソッドは最後に実行される。 |
テストクラスの解放メソッド |
afterClass |
@AfterClass |
ひとつのクラスに@AfterClass が指定されたメソッドとafterClass() メソッドが混在する場合、afterClass() メソッドは最後に実行される。 |
テストケースの初期化メソッド |
before |
@Before |
ひとつのクラスに@Before が指定されたメソッドとbefore() メソッドが混在する場合、before() メソッドは最後に実行される。 |
テストケースの解放メソッド |
after |
@After |
ひとつのクラスに@After が指定されたメソッドとafter() メソッドが混在する場合、after() メソッドは最後に実行される。 |
個々のテストメソッドに対応する初期化メソッド |
beforeXxx |
なし |
Xxxはテストメソッド名をあらわす。 |
個々のテストメソッドに対応する解放メソッド |
afterXxx |
なし |
Xxxはテストメソッド名をあらわす。 |
個々のテストメソッドに対応するフィールドバインディング直後のメソッド |
postBindFields |
@PostBindFields |
コンポーネントをテストクラスのフィールドにバインドした直後に実行される。 |
個々のテストメソッドに対応するフィールドアンバインディング直前のメソッド |
preUnbindFields |
@PreUnbindFields |
テストクラスのフィールドをアンバインドする直前に実行される。 |
テストメソッド |
上記のどの命名規則にも当てはまらないメソッド |
@Test |
|
自動フィールドバインディングとはコンテナに管理されたコンポーネントを特定のルールに従ってテストクラスのフィールドに自動でセットする機能です。自動フィールドバインディングのルールには以下のものがあります。
- バインディング対象のフィールドは非
static 、非final 、非プリミティブ型かつnull でなければならない。
- フィールド名からアンダースコア(_)を除いた名前のコンポーネントがコンテナに存在する場合、その名前のコンポーネントがフィールドにセットされる。
- フィールドの変数定義がインターフェース型でそのインタフェースをもつコンポーネントがコンテナに存在する場合、そのコンポーネントがフィールドにセットされる。
- フィールドに
@EJB が指定されbeanName もしくはname 要素に値が指定されている場合、その値を名前とするコンポーネントがコンテナに存在すれば、そのコンポーネントがフィールドにセットされる。
- 自動フィールドバインディングは、テストメソッド実行の直前に行われる(ただしTestContextインタフェースは例外的に
before() メソッド実行前にバインディングされる)。テストメソッドの実行が終了するとバインディングされたフィールドにはnull がセットされる。
自動トランザクションとは、すべてのテストメソッド実行前にトランザクションを自動で開始し、テストメソッド実行後にロールバックする機能です。この振る舞いが望ましくない場合は、@TxBehavior によりテストケースごとに振る舞いを変更できます。
自動インクルードとはs2junit4.diconに指定した設定ファイルと、命名規則に従った設定ファイルを自動でインクルードする機能です。命名規則はs2junit.diconで指定できます。自動インクルードが望ましくない場合は、テストコンテキストを利用することで無効にできます。
命名規則に従ったファイルに事前データを用意しておけば、自動でデータベースに反映できます。命名規則はs2junit4.diconで指定できます。事前データの自動準備が望ましくない場合は、テストコンテキストを利用することで無効にできます。
命名規則に従ったファイルに期待値を用意しておけば、テストコンテキストから期待値を取り出すことができます。命名規則はs2junit4.diconで指定できます。
テストはデフォルトでWARM deployモードで実行されます。WARM deployが適用されるにはconvention.dicon、creator.dicon、customizer.dicon
がCLASSPATH上に必要です。ただし、s2container.dicon が存在する場合はそこで SMART deploy の設定がされているものと見なし,WARM deploy は設定されません。WARM deployが望ましくない場合は、@WarmDeploy を使用することで無効にできます。WARM
deployについてはSMART deployを参照してください。
org.seasar.framework.unit.Seasar2クラスはorg.junit.runner.Runnerを継承したクラスです。@RunWithにこのランナーを指定することでS2JUnit4のテストクラスとして実行できるようになります。
@RunWith(Seasar2.class)
public class HogeTest {
・・・
}
S2JUnit4ではJUnit4のアサートメソッドに加えS2JUnit4独自のアサートメソッドを利用可能です。S2JUnit4独自のアサートメソッドはorg.seasar.framework.unit.S2Assertクラスに定義されています。S2Assertクラスはstaticインポートして利用してください。S2Assertクラスのアサートメソッドを使用すると以下のことが可能です。
- DataSetとDataSetの比較
- DataTableとDataTableの比較
- DataSetとBeanの比較
- DataSetとBeanのListの比較
- DataSetとBeanのMapの比較
- DataSetとEntityの比較
- DataSetとEntityのCollectionの比較
テストコンテキストはテストの実行時情報をもつオブジェクトです。テストコンテキストはorg.seasar.framework.unit.TestContextインタフェースで表されます。テストコンテキストの取得には自動フィールドバインディングを使用してください。TestContextの自動フィールドバインディングは他のコンポーネントとは異なりbefore()メソッド実行直前に行われます。TestContextインタフェースが提供する機能は次のものです。
- 設定ファイルのインクルード
- コンポーネントの登録
- 設定ファイルの自動インクルード機能の有効/無効の設定
- テストの事前データの準備方法の設定
- 期待値の取得
- 実行時テストクラス名の取得
- 実行時テストメソッド名の取得
- MockInterceptorの取得
@RunWith(Seasar2.class)
public class Hoge {
private TestContext ctx; //自動フィールドバインディングにより取得
private EmployeeDao dao;
public void before() {
ctx.setAutoIncluding(false); //自動インクルード機能を無効に設定
ctx.include("hoge.dicon"); //明示的なインクルード
ctx.register(EmployeeDaoImpl.class); //コンポーネントの登録
}
public void hoge() {
assertEquals(ctx.getExpected(), dao.getEmployee()); //期待値を取得
}
}
テストクラスからデータベースやExcelファイルのデータに簡単にアクセスできるようにS2JUnit4ではDataAccessorインタフェースを提供しています。自動フィールドバインディングにより取得し使用してください。
@RunWith(Seasar2.class)
public class Hoge {
private DataAccessor accessor; // 自動フィールドバインディングにより取得
public void hoge() {
DataTable table = accessor.readDbByTable("Employee");
DataSet dataSet = accessor.readXls("foo.xls");
...
}
}
アノテーションを利用することで、S2JUnit4のデフォルトの振る舞いの変更やモックの利用ができます。JUnit4のアノテーションはS2JUnit4においてもすべて利用可能です。以下ではSeasar2が提供するアノテーションについて説明します。
ルートコンテナを明示的に指定したい場合に使用します。@RootDicon のvalue 要素にはルートコンテナのdiconファイルのパスを指定します。
@RootDicon を使用する場合、@RootDicon に指定したdiconファイルを読み込んだコンテナがルートとなり、s2junit4.dicon を読み込んだコンテナがその子コンテナとなります。
@RootDicon を使用しない場合、s2junit4.dicon を読み込んだコンテナがルートコンテナとなります。
@RootDicon("app.dicon")
public void hoge() {
...
}
WARM deployの適用を制御する場合に指定します。@WarmDeploy のvalue 要素にはbooleanを指定します。false を指定するとWARM
deployは適用されません。@WarmDeploy はテストクラスとテストメソッドの両方に指定できます。両方に指定された場合、テストメソッドへの指定が優先されます。WARM
deployについてはSMART deployを参照してください。
@WarmDeploy(false)
public void hoge() {
...
}
テストの実行に当たって事前条件を指定したい場合に使用します。@Prerequisite のvalue 要素にはOGNL式を指定します。指定されたOGNL式がtrue と評価された場合にのみテストケースが実行されます。@Prerequisite はテストクラスとテストメソッドの両方に指定できます。両方に指定された場合、両方のOGNL式がtrue と評価される場合にのみテストケースが実行されます。事前条件が成り立たない場合、テストケースの実行はスキップされます。OGNLについてはOGNLガイドを参照してください。
@Prerequisite("isWindowsEnvironment()")
public void hoge() {
...
}
public boolean isWindowsEnvironment() {
return ...
}
トランザクションの振る舞いを変更したい場合に使用します。@TxBehavior のvalue 要素に列挙型TxBehaviorType の定数を指定します。@TxBhavior はテストクラスとテストメソッドの両方に指定できます。両方に指定された場合、テストメソッドへの指定が優先されます。
@TxBehavior(TxBehaviorType.COMMIT)
public void hoge() {
...
}
列挙型TxBehaviorType には以下の定数があります。
ROLLBACK
-
テストメソッド実行前にトランザクションを開始し、テストメソッド実行後にトランザクションをロールバックします。
- これは
@TxBehavior を使用しない場合の振る舞いと同じです。
COMMIT
-
テストメソッド実行前にトランザクションを開始し、テストメソッド実行後にトランザクションをコミットします。
NONE
-
テストメソッド実行前にトランザクションを開始しません。
Seasar2のMockInterceptorを使ったテストを実行したい場合に使用します。@Mock はメソッドに指定できます。targetName 要素に指定したコンポーネント名もしくはtarget 要素に指定したクラスをキーとしてコンポーネントを取得し、そのコンポーネントに対しMockInterceptorを適用します。returnValue 要素とthrowable 要素にはOGNL式を指定します。MockInterceptorについてはテスト技法を参照してください。OGNLについてはOGNLガイドを参照してください。
@Mock(target = Hello.class, pointcut = "greeting", returnValue = "'hello'")
public void hoge() {
...
}
ひとつのテストケースで@Mock を複数指定したい場合に使用します。
@Mocks({@Mock(target = Hello.class, pointcut = "greeting", returnValue = "'hello'"),
@Mock(target = Foo.class, returnValue = "true")})
public void hoge() {
...
}
EasyMockを利用したい場合に使用します。このアノテーションの使い方と説明についてはEasyMockサポートを参照してください。
PostBindFieldsアノテーションを利用すると、コンポーネントとテストクラスのフィールドをバインディングした直後に指定のメソッドを実行することができます。また、アノテーションを利用しないでpostBindFieldsという名前のメソッドを定義しても同様に実現できます。
@PostBindFields
public void hoge() {
...
}
preUnbindFieldsアノテーションを利用すると、テストクラスのフィールドをアンバインドする直前に指定のメソッドを実行することができます。また、アノテーションを利用しないでpreUnbindFieldsという名前のメソッドを定義しても同様に実現できます。
@preUnbindFields
public void fuga() {
...
}
@Prerequisite や@Mock で利用できるOGNL式では以下の変数を参照できます。
変数 |
説明 |
#this |
テストクラスのインスタンスを表します。 |
#method |
テストメソッドのインスタンスを表します。 |
#ENV |
環境設定ファイル(env_ut.txt )の値です.環境設定ファイルが存在しない場合は"ut" です. |
S2JUnit4を実行するには設定ファイルs2junit4.diconが必要です。s2junit4.diconでは、S2JUnit4の実行時に利用されるコンポーネントが定義されます。以下は、S2-Tigerの配布ファイルに含まれるs2junit4.diconです。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components namespace="s2junit4">
<component name="context" class="org.seasar.framework.unit.impl.InternalTestContextImpl"/>
<component class="org.seasar.framework.unit.impl.DataAccessorImpl"/>
<component class="org.seasar.framework.unit.impl.ConfigFileIncluderImpl">
<initMethod name="addConfigFile">
<arg>"javaee5.dicon"</arg>
</initMethod>
<initMethod name="addConfigFile">
<arg>context.testClassShortName + ".dicon"</arg>
</initMethod>
</component>
<component class="org.seasar.framework.unit.impl.TestDataPreparerImpl">
<initMethod name="addTestDataXlsPath">
<arg>context.testClassShortName + "_" + context.testMethodName + ".xls"</arg>
</initMethod>
<initMethod name="addTestDataXlsPath">
<arg>context.testClassShortName + ".xls"</arg>
</initMethod>
</component>
<component class="org.seasar.framework.unit.impl.ExpectedDataReaderImpl">
<initMethod name="addExpectedDataXlsPath">
<arg>context.testClassShortName + "_" + context.testMethodName + "_Expected" + ".xls"</arg>
</initMethod>
</component>
</components>
ここで定義される各コンポーネントの概略を説明をします。
- org.seasar.framework.unit.impl.InternalTestContextImpl
-
org.seasar.framework.unit.InternalTestContextインタフェースの実装です。org.seasar.framework.unit.InternalTestContextインタフェースの実装は必ずs2junit4.dicon内に定義される必要があります。このコンポーネントはテストの実行時情報を提供します。
- 他のコンポーネントはこのコンポーネントを参照することで実行時のテストクラスやテストメソッドの名称を使った命名規則を定義できます。
- org.seasar.framework.unit.impl..DataAccessorImpl
-
org.seasar.framework.unit.DataAccessorインタフェースの実装です。定義は必須ではありません。
データベースやExcelのデータにアクセスするためのメソッドを提供します。 - org.seasar.framework.unit.impl.ConfigFileIncluderImpl
-
org.seasar.framework.unit.ConfigFileIncluderインタフェースの実装です。定義は必須ではありません。
- このコンポーネントは設定ファイルのインクルードを行います。デフォルトではjavaee5.diconが固定で指定され、テストケースごとの設定ファイルが命名規約を用いて指定されています。
- org.seasar.framework.unit.impl.TestDataPreparerImpl
-
org.seasar.framework.unit.TestDataPreparerの実装です。定義は必須ではありません。
- このコンポーネントはExcelから読み取ったテストデータをデータベースに書き込みます。デフォルトでは事前データをもつExcelのパスが命名規則を用いて指定されています。
- org.seasar.framework.unit.impl.ExpectedReaderImpl
-
org.seasar.framework.unit.ExpectedReaderの実装です。定義は必須ではありません。
- このコンポーネントはExcelを読みDataSetを返します。デフォルトでは期待値をもつExcelのパスが命名規則を用いて指定されています。
上述のorg.seasar.framework.unit.impl.InternalTestContextImpl はサーブレットAPIのモック機能を持つためServlet APIに依存しています。
しかし、この機能が不要な場合org.seasar.framework.unit.impl.SimpleInternalTestContext を代わりに使用できます。
このクラスはデフォルトでJTAとEJB3を利用しない設定になっているため、
SimpleInternalTestContext を使うとServlet、JTA、EJB3のjarファイルなしでシンプルにS2JUnit4を実行できるようになります。
その場合のs2junit4.diconの設定は次のようになります。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components namespace="s2junit4">
<component name="context" class="org.seasar.framework.unit.impl.SimpleInternalTestContext"/>
<component class="org.seasar.framework.unit.impl.ConfigFileIncluderImpl">
<initMethod name="addConfigFile">
<arg>context.testClassShortName + ".dicon"</arg>
</initMethod>
</component>
</components>
s2junit4config.diconを使用することでS2JUnit4の内部的な振る舞いをカスタマイズできます(ここで使用するのはs2junit4.diconではなくs2junit4config.diconであることに注意してください)。s2junit4config.diconはCLASSPATHに含める必要があります。以下では、カスタマイズの例をいくつか紹介します。
S2JUnit4のメソッドの命名規則を変更したい場合はs2junit4config.diconを次のように記述します。ここではbefore、afterという名前の代わりにsetUp、tearDownを使うことを示しています。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components>
<component class="org.seasar.framework.unit.S2TestClassMethodsRunner$DefaultProvider"/>
<component class="org.seasar.framework.unit.impl.ConventionTestIntrospector">
<property name="beforeClassMethodName">"setUpClass"</property>
<property name="afterClassMethodName">"tearDownClass"</property>
<property name="beforeMethodName">"setUp"</property>
<property name="afterMethodName">"tearDown"</property>
</component>
</components>
S2JUnit4のメソッドの命名規則を無効にし、アノテーションでの指定のみを有効にしたい場合はs2junit4config.diconを次のように記述します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components>
<component class="org.seasar.framework.unit.S2TestClassMethodsRunner$DefaultProvider"/>
<component class="org.seasar.framework.unit.impl.AnnotationTestIntrospector"/>
</components>
知っていると便利なTipsを紹介します。
テストケースの名前を知るにはTestContext#getTestMethodName() を利用します。
TestContext ctx;
public void test() throws Exception {
String methodName = ctx.getTestMethodName();
...
}
デフォルトではExcelセルの値に含まれる両端の空白は除去されます。
空白文字として認識させるにはExcelセルの値を"ABC " のようにダブルクォテーションで囲み、
テストクラスのbefore メソッドやbeforeXxx メソッド内でTestContext#setTrimString()
を呼びfalse を設定します。空白文字が除去されなくなります。
TestContext ctx;
public void before() {
ctx.setTrimString(false);
}
この設定をデフォルトとしてすべてのテストケースに反映させたい場合は、s2junit4.dicon を変更します。
<component name="context" class="org.seasar.framework.unit.impl.InternalTestContextImpl">
<property name="trimString">false</property>
</component>
テストデータの準備方法(追加、置換など)は変更することができます。
データをテーブルごとに完全に置き換えたい場合は、テストクラスのbefore メソッドやbeforeXxx メソッド内でTestContext#setPreparationType()
を呼びPreparationType.ALL_REPLACE を設定します。
TestContext ctx;
public void before() {
ctx.setPreparationType(PreparationType.ALL_REPLACE);
}
この設定をデフォルトとしてすべてのテストケースに反映させたい場合は、s2junit4.dicon を変更します。
<component name="context" class="org.seasar.framework.unit.impl.InternalTestContextImpl">
<property name="preparationType">@org.seasar.framework.unit.PreparationType@ALL_REPLACE</property>
</component>
列挙型PreparationType にはALL_REPLACE の他にNONE 、WRITE 、REPLACE
が定義されています。
|