SAStrutsを学ぶ – まずはS2Junit4でServiceをUnitテスト

感想おまちしてます!

藤原のようなStruts1.xを使っていた世代は、DIを覚えなきゃやばいと感じたので、SAStrutsを勉強している。
どうも、最近のJavaフレームワークをみていると、Struts2は死にかけていて、スタンダートとしてSpring。国内なら国産のSeaserみたい。Google Guiceも、生まれたてなのに結構奮闘している。

まずはSAStrutsをと思い、その中でも業務にかかわってきそうなUnitテストの動きを調べてみる。

スポンサーリンク

テストネタ

まずは、1分でWebアプリを作れるEclipseプラグイン「Dolteng」を読みながら、H2データベースを使って、実際にSAStrutsを動かしてみる。

ここで自動作成されたクラスはAction、Service、Entity、Formとあり、DB接続やWeb画面まであるので、これらに対して単体テストをおこなってみる。

単体テストを作ってみる

S2JUnit4のページを読んだが、すんなりできなかった部分があったので、自分のやった手順をまとめる。

まず、1分でWebアプリを作れるEclipseプラグイン「Dolteng」ではDeptというテーブルとEmpというテーブルがありそれぞれにサンプルデータが少し入っている。今回はDeptServiceのクラスを作る。このクラスはfindByIdでIDに紐づいたデータを取ってくるServiceクラス。

まず、s2junit4pluginのページを参考にテストケースクラスとdiconファイルを作成する。テストケースクラスである「DeptServiceTest 」にはテストを実装する。

 package com.daipresents.sastruts.service;
import static org.junit.Assert.assertNull;
import static org.seasar.framework.unit.S2Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.seasar.framework.unit.Seasar2;
import org.seasar.framework.unit.TestContext;
import com.daipresents.sastruts.entity.Dept;
@RunWith(Seasar2.class)
public class DeptServiceTest {
private TestContext ctx;
private DeptService deptService;
/**
* {@link com.daipresents.sastruts.service.DeptService#findById(java.lang.Long)} のためのテスト・メソッド。
*/
@Test
public void testFindById() {
Dept dept = deptService.findById(new Long(90000));
assertNull(ctx.getTestMethodName() + ":データが存在しない場合", dept);
dept = deptService.findById(new Long(90001));
assertEquals(ctx.getTestMethodName() + ":データが存在する場合", ctx.getExpected(), dept);
}
}

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></components>

ただ、「<include path=”app.dicon”/>」を書かないとS2JDBC + S2JUnit4でハマると同じ現象がでたんだけど気のせいかもしれない。Seasar S2JUnit4 – @//メモでは書いてあるんだけど、消しても動いてしまった。
「src/test/resources/s2junit4.dicon」に

 <component class="org.seasar.framework.unit.impl.ConfigFileIncluderImpl">
<initMethod name="addConfigFile">
<arg>"app.dicon"</arg>
</initMethod>
<initMethod name="addConfigFile">
<arg>context.testClassShortName + ".dicon"</arg>
</initMethod>
</component>

と書かれているので、消しても動くんだと思うけど。どっちだろうね。

で、テストと同じ場所に「DeptServiceTesttestFindById.xls」と「DeptServiceTesttestFindById_Expected.xls」を作る。

前者はテスト時の初期データとなり、後者は予想するデータとなる。
今回テストするクラスはデータを参照するだけなので、両ファイルとも同じ内容になる。
データは以下のような感じ。必須の値はすべて埋めないと、Insertしたときにエラーとなる。

001 (2)

ここまできたら、H2データベースを起動、Tomcatを起動して、DeptServiceTestをEclipseで開いて「Ctrl+0」を押せばテストが始まる。

Eclipseにでるログはこんな感じです。

 設定ファイル(com/daipresents/sastruts/service/DeptServiceTest.dicon)をインクルードします
S2Containerを作成します。path=com/daipresents/sastruts/service/DeptServiceTest.dicon
S2Containerを作成しました。path=com/daipresents/sastruts/service/DeptServiceTest.dicon
クラス(com.daipresents.sastruts.service.DeptService[deptService])のコンポーネント定義を登録します
トランザクションを開始しました。tx=[FormatId=4360, GlobalId=1260108703621/0, BranchId=]
Excelファイル(com/daipresents/sastruts/service/DeptServiceTest_testFindById.xls)の値をデータベースに追加します
物理的なコネクションを取得しました
論理的なコネクションを取得しました。tx=[FormatId=4360, GlobalId=1260108703621/0, BranchId=]
論理的なコネクションを閉じました。tx=[FormatId=4360, GlobalId=1260108703621/0, BranchId=]
論理的なコネクションを取得しました。tx=[FormatId=4360, GlobalId=1260108703621/0, BranchId=]
INSERT INTO dept (ID, DEPT_NO, DEPT_NAME, LOC, VERSION_NO) VALUES (90001, 50, '総務課', '大阪', 1)
論理的なコネクションを閉じました。tx=[FormatId=4360, GlobalId=1260108703621/0, BranchId=]
論理的なコネクションを取得しました。tx=[FormatId=4360, GlobalId=1260108703621/0, BranchId=]
INSERT INTO dept (ID, DEPT_NO, DEPT_NAME, LOC, VERSION_NO) VALUES (90002, 60, '人事課', '名古屋', 1)
論理的なコネクションを閉じました。tx=[FormatId=4360, GlobalId=1260108703621/0, BranchId=]
BEGIN com.daipresents.sastruts.service.DeptService#findById(90000)
select T1_.ID as C1_, T1_.DEPT_NO as C2_, T1_.DEPT_NAME as C3_, T1_.LOC as C4_, T1_.VERSION_NO as C5_ from DEPT T1_ where T1_.ID = 90000
論理的なコネクションを取得しました。tx=[FormatId=4360, GlobalId=1260108703621/0, BranchId=]
END com.daipresents.sastruts.service.DeptService#findById(90000) : null
BEGIN com.daipresents.sastruts.service.DeptService#findById(90001)
select T1_.ID as C1_, T1_.DEPT_NO as C2_, T1_.DEPT_NAME as C3_, T1_.LOC as C4_, T1_.VERSION_NO as C5_ from DEPT T1_ where T1_.ID = 90001
END com.daipresents.sastruts.service.DeptService#findById(90001) : com.daipresents.sastruts.entity.Dept@19ea173
Excelファイル(com/daipresents/sastruts/service/DeptServiceTest_testFindById_Expected.xls)を期待値として読み込みます
トランザクションをロールバックしました。tx=[FormatId=4360, GlobalId=1260108703621/0, BranchId=]
論理的なコネクションを閉じました。tx=[FormatId=4360, GlobalId=1260108703621/0, BranchId=]
物理的なコネクションを閉じました
環境変数#Envにファイル(env.txt)から値(ct)が設定されました

データを取り込んで、それに対してテスト。結果を予期したデータと比較している。

まとめ

DBアクセスしているServiceクラスの単体テストは成功。DBUnitみたいなもんかということで、DIのテストという感じがしなかった。

また、今回の場合、findByIdでデータを取ってくるが、このメソッドだと、データが存在すれば1レコードがかえってきて、存在しなければnullがかえってくる。
よって、予期するデータに複数件レコードをいれてしまうと、「レコードが2つもあるよ!」と怒られる。
初期データは使いまわせる気がするが、予期するデータはテストメソッドごとに必要になる。

ここからActionとかもテストしてみる予定だけど、S2JUnitってはやってないせいか、Googleの検索結果が少ないのが気になる。

余談

Excel2007をEclipseから開いて保存するとEclipseが固まりPCもブルーバックになったので、みんな気をつけろ!

また、Excelをエクスプローラから開いて編集しても、Eclipseでそのファイルを更新しないと、テストに反映されないみたい。F5連打はした方がいいかも。