S2JUnit4はJUnit4を拡張したテスティングフレームワークです。
S2JUnit4はアノテーションと命名規則をバランス良く組み合わせ少ないコードで効率のよいテストを可能にします。
S2JUnit4の主な機能には次のものがあります。
- 命名規則に従い初期化メソッドやテストメソッドを適切な順序で呼び出します。JUnit4の@Beforeや@Testなどのアノテーションと併用することも可能です。
- テストメソッド実行のための事前条件を指定できます。
- テストメソッド実行時のトランザクションの振る舞いを指定できます。デフォルトでは自動で開始され自動でロールバックされます。
- 複数の設定ファイルを自動でインクルードできます。
- テストごとに異なるテストデータを自動で準備できます。デフォルトではExcelに用意した値をデータベースに書き込みます。
- テストごとに異なるテストの期待値を簡単に取得できます。デフォルトではExcelに用意した期待値を取得します。
- テストメソッド実行直前にコンポーネントを自動でテストクラスのフィールドにセットできます。EJB3のコンポーネントをフィールドにセットすることもできます。
- BeanやEntityとDataSetを比較するためのアサートメソッドを提供します。
S2Container 2.4.0 rc2 以上と S2Tiger 2.4.0 rc2 以上が必要です。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が相当します。
- org.seasar.framework.unit.S2Assert.assertEqualsをstaticインポートします。assertEqualsメソッドはテストメソッドgetEmployee()内でDataSetとBeanを比較するために使用します。
- テストメソッドgetEmployee()を用意します。テストメソッドは
public 、非static 、引数なし、戻り値なしでなければいけません。テストメソッド実行前にはトランザクションが自動で開始され、実行後にはトランザクションが自動でロールバックされます。
- 実行したくないテストメソッドには@Ignoreを指定します。これはJUnit4のアノテーションです。S2JUnit4ではJUnit4のアノテーションをそのまま利用できます。
このテストクラスは設定ファイル、準備値、期待値を自動で読み込みます。
- デフォルトの設定では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に存在する場合はそのアノテーションを示しています。命名規則はカスタマイズできます。カスタマイズの方法はS2JUnit4のカスタマイズを参照ください。
メソッドの種類 |
JUnit4のアノテーション |
S2JUnit4の命名規則 |
備考 |
テストクラスの初期化メソッド |
@BeforeClass |
beforeClass |
ひとつのクラスに@BeforeClassが指定されたメソッドとbeforeClass()メソッドが混在する場合、beforeClass()メソッドは最後に実行される。 |
テストクラスの解放メソッド |
@AfterClass |
afterClass |
ひとつのクラスに@AfterClassが指定されたメソッドとafterClass()メソッドが混在する場合、beforeClass()メソッドは最後に実行される。 |
テストケースの初期化メソッド |
@Before |
before |
ひとつのクラスに@Beforeが指定されたメソッドとbefore()メソッドが混在する場合、before()メソッドは最後に実行される。 |
テストケースの解放メソッド |
@After |
after |
ひとつのクラスに@Afterが指定されたメソッドとbeforeClass()メソッドが混在する場合、after()メソッドは最後に実行される。 |
個々のテストメソッドに対応する初期化メソッド |
- |
beforeXxx |
Xxxはテストメソッド名をあらわす。 |
個々のテストメソッドに対応する解放メソッド |
- |
afterXxx |
Xxxはテストメソッド名をあらわす。 |
テストメソッド |
@Test |
上記のどの命名規則にも当てはまらないメソッド |
|
メソッドのシグネチャはすべてpublic 、引数なし、戻り値なしでなければいけません。さらに、テストクラスの初期化/解放メソッドはstatic 、それ以外のメソッドは非static でなければいけません。
JUnit4のアノテーションとS2JUnit4の命名規則が1つのメソッドに対して同時に使用される場合、命名規則よりもアノテーションが優先されます。
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の比較
自動フィールドバインディングとはコンテナに管理されたコンポーネントを特定のルールに従ってテストクラスのフィールドに自動でセットする機能です。自動フィールドバインディングのルールには以下のものがあります。
- バインディング対象のフィールドは非
static 、非final 、非プリミティブ型かつnull でなければならない。
- フィールド名からアンダースコア(_)を除いた名前のコンポーネントがコンテナに存在する場合、その名前のコンポーネントがフィールドにセットされる。
- フィールドの変数定義がインターフェース型でそのインタフェースをもつコンポーネントがコンテナに存在する場合、そのコンポーネントがフィールドにセットされる。
- フィールドにEJBアノテーションが指定されbeanNameもしくはname要素に値が指定されている場合、その値を名前とするコンポーネントがコンテナに存在すれば、そのコンポーネントがフィールドにセットされる。
- 自動フィールドバインディングは、テストメソッド実行の直前に行われる(ただしTestContextインタフェースは例外的にbefore()メソッド実行前にバインディングされる)。テストメソッドの実行が終了するとバインディングされたフィールドにはnullがセットされる。
テストコンテキストはテストの実行時情報をもちS2JUnit4の振る舞いを制御するオブジェクトです。テストクラスはorg.seasar.framework.unit.TestContextインタフェースを介してテストコンテキストにアクセスできます。テストコンテキストの取得には自動フィールドバインディングを使用してください。自動フィールドバインディングは他のコンポーネントとは異なりbefore()メソッド実行直前に行われます。TestContextインタフェースが提供する機能は次のものです。
- 設定ファイルのインクルード
- コンポーネントの登録
- 設定ファイルの自動インクルード機能の有効/無効の設定
- テストデータの自動準備機能の有効/無効の設定
- 期待値の取得
- 実行時テストクラス名の取得
- 実行時テストメソッド名の取得
@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");
...
}
}
テストクラスまたはテストメソッドにorg.seasar.framework.unit.Prerequisiteアノテーションを指定することで事前条件を示すことができます。事前条件はOGNL式で記述します。OGNLについてはOGNLガイドを参照してください。
OGNL式の評価がtrue となると事前条件が満たされたと判定されます。事前条件が満たされない場合、テストメソッドは実行されず無視されます。テストクラスとテストメソッドの両方にこのアノテーションが指定された場合、テストメソッドの実行には両方の事前条件が満たされる必要があります。
@Prerequisite("isWindowsEnvironment()")
public void hoge() {
...
}
public boolean isWindowsEnvironment() {
return ...
}
トランザクションの振る舞いのデフォルトの挙動は、「テストメソッドの実行前にトランザクションが自動で開始され、テストメソッドの実行後に自動でロールバックされる」というものです。この挙動はテストクラスまたはテストメソッドにorg.seasar.framework.unit.TxBehaviorアノテーションを指定することで変更できます。
TxBehaviorアノテーションにTxBehaviorType.COMMITを指定した場合、テストメソッドの実行前にトランザクションが自動で開始され、テストメソッド実行後に自動でコミットされます。
TxBehaviorアノテーションにTxBehaviorType.ROLLBACKを指定した場合、アノテーションを指定しない場合と同じ挙動になります。
TxBehaviorアノテーションにTxBehaviorType.NONEを指定した場合、トランザクションは開始されません。
このアノテーションをテストクラスとテストメソッドの両方に指定した場合はテストメソッドのアノテーションが優先されます。
@TxBehavior(TxBehaviorType.COMMIT)
public void hoge() {
...
}
@TxBehavior(TxBehaviorType.ROLLBACK)
public void foo() {
...
}
@TxBehavior(TxBehaviorType.NONE)
public void bar() {
...
}
s2junit.diconにはS2JUnit4が実行時に参照するコンポーネントが定義されています。このファイルで設定できる項目は主に次のものです。
- 設定ファイルの自動インクルード機能の有効/無効の設定
- テストデータの自動準備機能の有効/無効の設定
- 自動インクルード対象の設定ファイルの指定
- 準備値のExcelファイルのパスの指定
- 準備値をデータベースに追加するときの追加/置き換えの設定
- 期待値のExcelファイルのパスの指定
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インタフェースの実装です。
設定ファイルの自動インクルードを無効にしたい場合はautoIncludingプロパティにfalseを設定してください。
テストデータの自動準備を無効にしたい場合は、autoPreparingプロパティをfalseに設定してください。
このコンポーネントがもつテストクラス名やテストメソッド名の情報は他のコンポーネントの設定に利用できます。 - org.seasar.framework.unit.impl..DataAccessorImpl
-
org.seasar.framework.unit.DataAccessorインタフェースの実装です。
データベースやExcelのデータにアクセスするためのメソッドを持ちます。
- org.seasar.framework.unit.impl.ConfigFileIncluderImpl
-
org.seasar.framework.unit.ConfigFileIncluderインタフェースの実装です。このコンポーネントは設定ファイルのインクルードを行います。
addConfigFileメソッドで自動インクルードしたい設定ファイルの名前を指定してください。テストクラスと同一パッケージに存在する場合はパッケージ部分のパスは省略できます。自動インクルードが有効であれば、テストメソッド実行前にここで指定されたすべての設定ファイルが自動でインクルードされます。
- org.seasar.framework.unit.impl.TestDataPreparerImpl
-
org.seasar.framework.unit.TestDataPreparerの実装です。このコンポーネントはExcelから読み取ったテストデータをデータベースに書き込みます。
addTestDataXlsPathメソッドでテストデータをもつExcelファイル名を指定してください。テストクラスと同一パッケージに存在する場合はパッケージ部分のパスは省略できます。
テストデータの自動準備が有効であれば、テストメソッド実行前に追加された順で存在チェックを行い最初に存在が確認されたExcelファイルのデータをデータベースに書き込まれます。
追加ではなくテストデータを置き換えをしたい場合はreplaceDbプロパティにture を設定してください。
- org.seasar.framework.unit.impl.ExpectedReaderImpl
-
org.seasar.framework.unit.ExpectedReaderの実装です。このコンポーネントはExcelを読みDataSetを返します。
addExpectedDataXlsPathで期待値をもつExcelのパス名を指定してください。テストクラスと同一パッケージに存在する場合はパッケージ部分のパスは省略できます。TestContext#getExpected()メソッドが実行されると追加された順で存在チェックを行い最初に存在が確認されたExcelファイルをDataSetに変換して返します。
s2junit4config.diconを使いS2JUnit4を実行するコンポーネントの設定を変更することで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の命名規則を無効にしたい場合は次のように記述します。こうするとJUnit4のアノテーションを明示的に指定したメソッドのみが処理の対象となります。
<?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>
テスト実行処理のカスタマイズ
S2JUnit4のテスト実行に関する一連の処理順序や内容をカスタマイズしたい場合(たとえば自動フィールドバインディングの挙動を変更したいなど)は、org.seasar.framework.unit.S2TestClassMethodsRunner$DefaultProviderクラスのtestMethodRunnerClassプロパティを変更します。このプロパティにはテストメソッドの実行処理を記述したクラスを指定できます。ただし、このクラスはorg.seasar.framework.unit.S2TestMethodRunnerを継承する必要があります。
<?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">
<property name="testMethodRunnerClass">@hoge.Foo@class</property>
</component>
</components>
|