Spring FrameworkのUnitテスト実装方法 1-4.Repositoryテスト(Junit4, spring-test, DBUnit, spring-test-dbunit)
TERASOLUNA Server Framework for Java (5.x) Development Guideline
サンプルソースはこちら
(これのMyBatis3を使用したパターンで作成してます。)
1-4.Repositoryテスト Junit4,Spring-test,DBUnit,spring-test-dbunitライブラリを使用したパターン
一個ずつライブラリが増えてる・・・w
ちょっとしか変わんないんじゃね?って思うでしょ。ちゃんと違うから!
1-3の時のDBのセットアップではDBUnitライブラリを使いました。
けど、@overrideが必要なメソッドであったりxmlを読み込ませる設定を書かなければいけなかったりしてまだちょっと面倒です。
そう思っている人(自分含めて)のために、SpringでDBunitを使う時に今や必須ともいえるライブラリ。
それがspring-test-dbunitです。
spring-test-dbunitは「spring」っていう名前がついているので、Spring Frameworkの公式ライブラリかと思ってしまいますが違います。けどSpringの中の人が個人的に作っているので同じっちゃ同じか。
【pom.xml】
<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>org.dbunit</groupId> <artifactId>dbunit</artifactId> <version>2.5.4</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); }
【テストクラス】
TodoRepositoryTestVerSpringTestDBunit.java
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:META-INF/spring/test-context-dbunit.xml"}) //@TestExecutionListeners・・・TestContextManagerに指定したListenerを設定する。 //正直あまり良くわかってない。spring-test-dbunitを使う時はお決まり的なやつらしい。 //TransactionDbUnitTestExecutionListenerがspring-test-dbunitで必要だからかも。 @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionDbUnitTestExecutionListener.class, SqlScriptsTestExecutionListener.class }) @Transactional public class TodoRepositoryTestVerSpringTestDBunit { @Inject TodoRepository target; @Inject JdbcTemplate jdbctemplate; @Before public void setUp() { //spring-test-dbunitアノテーションでセットアップと比較を行うため、処理なし } @Test //@DatabaseSetup・・・spring-test-dbuniライブラリのアノテーション。テスト実行前にデータをセットアップしてくれる。テストクラスごと、メソッドごとでの指定が可能 @DatabaseSetup("classpath:META-INF/dbunit/test_data.xml") //@ExpectedDatabase・・・spring-test-dbuniライブラリのアノテーション。テストメソッド実行後のテーブルの状態を指定したファイルと比較検証してくれる。 //エラーの時はJunitの例のバーが赤くなる。 @ExpectedDatabase(value="classpath:META-INF/dbunit/compare_data.xml", assertionMode = DatabaseAssertionMode.NON_STRICT) public void testUpdate() { //テスト用のデータを作成(getTodoDataメソッドはDBからデータを取得するprivateメソッド。取得したデータを書き換えて更新する。) String todoId = "cceae402-c5b1-440f-bae2-7bee19dc17fb"; Todo testDataTodo = getTodoData(todoId); testDataTodo.setFinished(true); //updateメソッドのテスト boolean actTodo = target.update(testDataTodo); //結果検証 assertEquals(actTodo, true); } }
spring-test-dbunitとして使用しているのは以下
・TransactionDbUnitTestExecutionListener.class
→@TestExecutionListenersで設定している。
・@DatabaseSetup
→ファイルを指定するだけでxmlが一発で読み込まれる。面倒な設定不要!(デフォだとxmlファイルの読み込み)
・@ExpectedDatabase
→ファイルを指定するだけでテスト後のデータが一発で比較される。面倒な設定不要!(デフォだとxmlファイルの読み込み)
・@DatabaseSetupの引数として設定しているassertionModeは他にも使えるモードがある。他の人がもっと分かりやすくまとめているので詳細はそっちの方で・・・
【その他設定】
test-context-dbunit.xml
DBunitを有効にするために少しtest-context.xml(1-1参照)を書き換えてます。
これをしないとDBunitがうまく動いてくれないらしい・・・
<bean id="log4jDataSource" class="net.sf.log4jdbc.Log4jdbcProxyDataSource"> <constructor-arg index="0" ref="realDataSource" /> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy"> <constructor-arg index="0" ref="log4jDataSource" /> </bean>
test_data.xml
セットアップ用のデータです。
<?xml version='1.0' encoding='UTF-8'?> <dataset> <TODO TODO_ID="cceae402-c5b1-440f-bae2-7bee19dc17fb" TODO_TITLE="one" FINISHED="false" CREATED_AT="2017-10-01 15:39:17.888" /> <TODO TODO_ID="5dd4ba78-ff5b-423b-aa2a-a07118aeaf90" TODO_TITLE="two" FINISHED="false" CREATED_AT="2017-10-01 15:39:19.981" /> <TODO TODO_ID="e3bdb9af-3dde-40b7-b5fb-4b388567ab45" TODO_TITLE="three" FINISHED="false" CREATED_AT="2017-10-01 15:39:28.437" /> </dataset>
compare_data.xml
テストメソッド実行後の検証用データです。
テストメソッドの内容が、ID”cceae402-c5b1-440f-bae2-7bee19dc17fb”のデータに対してFinishedをtrueにする内容なので、
test_data.xmlの内容からID=”cceae402-c5b1-440f-bae2-7bee19dc17fb”のFINISHEDを”true”に変更しています。
<?xml version='1.0' encoding='UTF-8'?> <dataset> <TODO TODO_ID="5dd4ba78-ff5b-423b-aa2a-a07118aeaf90" TODO_TITLE="two" FINISHED="false" CREATED_AT="2017-10-01 15:39:19.981" /> <TODO TODO_ID="cceae402-c5b1-440f-bae2-7bee19dc17fb" TODO_TITLE="one" FINISHED="true" CREATED_AT="2017-10-01 15:39:17.888" /> <TODO TODO_ID="e3bdb9af-3dde-40b7-b5fb-4b388567ab45" TODO_TITLE="three" FINISHED="false" CREATED_AT="2017-10-01 15:39:28.437" /> </dataset>
1-3と比べてもらえると分かりやすいと思いますが、テストコードの記述が格段に減ってます。使わない手は無いですね!!
サンプルソースはgithubで公開してます。
Spring Unit Test pattern4