Spring FrameworkのUnitテスト実装方法 1-7.Repositoryテスト(Junit4, spring-test, DBSetup)
TERASOLUNA Server Framework for Java (5.x) Development Guideline
サンプルソースはこちら
(これのMyBatis3を使用したパターンで作成してます。)
1-7.Repositoryテスト Junit4,Spring-test,DBSetupライブラリを使用したパターン
DBSetupライブラリはその名の通り、テスト時にデータをセットアップするためのライブラリです。
DBUnitを使用した場合と比較すると、メリットは
・Javaファイルでセットアップデータの記述ができるので、xmlファイルが不要になる。
・同じデータの繰り返していわゆるデータを「膨らます」場合、膨らませるデータの個数や設定を制御できるのでxmlファイルに同じデータをコピペしなくて済むから楽。
・セットアップをし直すかどうかの制御ができるので、必要最小限のセットアップで済みその分動作が軽くなる。
などが挙げられるでしょうか・・・?
一方で、デメリットとしては
・セットアップ用のライブラリなので、DBunitでいうところの@ExpectedDatabaseといった検証用の機能は無い(調べたらあるかもですが。)
・DBunitと比べて使っている人が少な目
といったところかも。
【pom.xml】
DBSetupライブラリが必要になります。
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.ninja-squad</groupId> <artifactId>DbSetup</artifactId> <version>2.1.0</version> <scope>test</scope> </dependency> </dependencies>
【テスト対象クラス】
TodoRepository.java
テスト対象はupdateメソッド
public interface TodoRepository { Todo findOne(String todoId); Collection<Todo> findAll(); void create(Todo todo); boolean update(Todo todo); void delete(Todo todo); long countByFinished(boolean finished); }
【テストクラス】
TodoRepositoryTestVerDBsetup.java
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:META-INF/spring/test-context.xml"}) @Transactional public class TodoRepositoryTestVerDBsetup { @Inject TodoRepository target; @Inject DataSource dataSource; @Inject JdbcTemplate jdbctemplate; private static DbSetupTracker TRACKER = new DbSetupTracker(); @Before public void setUp() { //DBSetupを使う準備 Destination dest = new DataSourceDestination(dataSource); //sequenceOfメソッドで囲むことによって、連続してデータに対しての処理を実行できる。 Operation ops = Operations.sequenceOf(DbsetupOperations.INIT_TABLE, DbsetupOperations.SETUP_TABLE_A); DbSetup dbSetup = new DbSetup(dest, ops); //TRACKERでセットアップを制御する。 TRACKER.launchIfNecessary(dbSetup); } @Test public void testUpdate() throws Exception{ //テスト用のデータを作成(getTodoDataメソッドはDBからデータを取得するprivateメソッド。取得したデータを書き換えて更新する。) SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); String todoId = "cceae402-c5b1-440f-bae2-7bee19dc17fb"; Todo testDataTodo = getTodoData(todoId); testDataTodo.setFinished(true); //updateメソッドのテスト boolean actTodo = target.update(testDataTodo); //結果検証 assertEquals(actTodo, true); //期待値の作成 Todo exptodo = new Todo(); exptodo.setTodoId("cceae402-c5b1-440f-bae2-7bee19dc17fb"); exptodo.setTodoTitle("one"); exptodo.setFinished(true); String strDate = "2017-10-01 15:39:17.888"; Date date = sdFormat.parse(strDate); exptodo.setCreatedAt(date); //処理後データの取得(getTodoDataメソッドはDBからテスト後に変更されたデータを取得するprivateメソッド) Todo actTestDataTodo = getTodoData(todoId); //メソッド実行後テーブルデータ検証 //date型の表示形式が異なるため、時刻文字列に変換して比較している assertEquals(exptodo.getTodoId(), actTestDataTodo.getTodoId()); assertEquals(exptodo.getTodoTitle(), actTestDataTodo.getTodoTitle()); assertEquals(exptodo.isFinished(), actTestDataTodo.isFinished()); assertEquals(sdFormat.format(exptodo.getCreatedAt()) ,sdFormat.format(actTestDataTodo.getCreatedAt())); /* * TRACKERによるセットアップの制御 * テスト対象が参照系(select)の場合・・・参照のみでありデータベースを更新していないので、データの再セットアップは不要。 * テスト対象が更新系(insert, update, delete)の場合・・・更新されたデータベースのままだと次のテストに影響を与えるので、データのセットアップをし直す必要がある。 * TRACKER.skipNextLaunchメソッドを実行することで、次のテスト(@Test)の前に実行される@Beforeのデータセットアップ処理をスキップすることができる。 * * 今回はupdateメソッドがテスト対象のため、TRACKER.skipNextLaunch();をコメントアウトしている。 */ //TRACKER.skipNextLaunch(); } }
コメントにも書いてありますが、DBSetupのポイントとしてはセットアップの制御をTRACKERで行っている点です。
DBSetupを使う上で必須というわけではないのですが、この制御によって不必要なセットアップを減らすことができます。
更新系の処理の時にのみデータの再セットアップを行って綺麗な状態で始める・・・といったことが可能です。
DbsetupOperations.java
public class DbsetupOperations { /* * テーブルデータを初期化するメソッド */ public static final Operation INIT_TABLE = Operations.sequenceOf( Operations.deleteAllFrom("todo")); /* * テーブルにデータをセットアップするメソッド(パターンA) */ public static final Operation SETUP_TABLE_A = Operations.sequenceOf( Operations.insertInto("todo") .columns("todo_id","todo_title","finished","created_at") .values("cceae402-c5b1-440f-bae2-7bee19dc17fb","one",false,"2017-10-01 15:39:17.888") .values("5dd4ba78-ff5b-423b-aa2a-a07118aeaf90","two",false,"2017-10-01 15:39:19.981") .values("e3bdb9af-3dde-40b7-b5fb-4b388567ab45","three",false,"2017-10-01 15:39:28.437") .build() ); /* * テーブルにデータをセットアップするメソッド(パターンB) * パターンAとの違いは1行ずつカラムに対してデータを指定している点。 */ public static final Operation SETUP_TABLE_B = Operations.sequenceOf( Operations.insertInto("todo") //row0 .row() .column("todo_id", "cceae402-c5b1-440f-bae2-7bee19dc17fb") .column("todo_title", "one") .column("finished", false) .column("created_at", "2017-10-01 15:39:17.888") .end() //row1 .row() .column("todo_id", "5dd4ba78-ff5b-423b-aa2a-a07118aeaf90") .column("todo_title", "two") .column("finished", false) .column("created_at", "2017-10-01 15:39:19.981") .end() //row2 .row() .column("todo_id", "e3bdb9af-3dde-40b7-b5fb-4b388567ab45") .column("todo_title", "three") .column("finished", false) .column("created_at", "2017-10-01 15:39:28.437") .end() .build() ); }
データセットアップ用のクラスです。
これ以外にデータの膨らまし用に使える処理とかもあります。
詳しくはDBSetupの本家HPに。。。
【その他設定】
test-context.xml
JdbcTemplateを使えるように設定します。(データ検証用のため)
基本はapplicationContext.xmlの設定を丸パクリして、不必要なものを削った感じです。
<!-- jdbcTemplateの設定 --> <bean class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource" /> </bean>
xmlとかもう見るだけで虫唾が走って無理だわ!!という方にはおすすめかもしれませんw
何より「流れるようなインタフェース」で作成するというコンセプトがカッコいいし便利だし楽です。
サンプルソースはgithubで公開してます。
Spring Unit Test pattern7