Spring+Domaで実装したアプリのユニットテスト
DBアクセスまわりのコードのユニットテストを書く場合、データベースに入っているデータが変わるとテストが失敗するようになるので、
ファイルからテストデータを投入したい事があると思います。
やりたい事
- テストメソッドの実行前に、データベースの既存データをDELETEして、ファイルからテストデータを投入。
- テストメソッドが終わったらロールバック。
ただし、コミットしたい場合もあるので、ロールバックするかコミットするかは、メソッド毎に決められるようにしたい。
使用するライブラリ
- Spring
- DbUnit
DBまわり(Daoなど)のユニットテスト支援ツール - Spring Test DBUnit
DbUnitとSpringの連携ライブラリ - DbUnitNG
DomaのSELECT結果のBeanListと、DbUnitのDataSetをAssert出来るようにするライブラリ
便利なライブラリありがとうございます!
DbUnitをカスタマイズして使った理由
- commons-langを使ってありましたが、僕は最近はLombokとGuavaを使っているのでcommons-langを入れたくなかった
- 僕はDomaのEntityをSNAKE_UPPER_CASEモードで使ってるのですが、DbUnintNGではBeanのフィールド名をtoLowerCaseしてあるので、Assertion.assertEqualsでfailする。
例えば、employeeId
がemployeeid
となってしまう。
employee_id
と変換する必要があった。
ソースはこんな感じです。
- RunWithアノテーションでSpringJUnit4ClassRunnerを指定します。
- ContextConfigurationアノテーションでテスト対象クラスと、テスト対象クラスの依存クラスを指定します。
- TransactionConfigurationアノテーション、Transactionalアノテーション、TestExecutionListenersアノテーションはお決まりのやつです。
- テストメソッドに対して、DatabaseSetupアノテーションで投入するテストデータを指定します。
テストデータのファイルはテストクラスと同じパッケージに配置します - テスト対象が検索メソッドの場合は、IDataSetに自分で期待値データを読み込み、Assertion.assertEqualsでassertします。
この場合、期待値データのファイルはパスを指定して読み込む必要があるため、resolvePhysicalDirectoryというメソッドを作ってます - テスト対象が更新系メソッドのテストの場合は、ExpectedDatabaseアノテーションで更新後のDBとassertする期待値ファイルを指定します。
期待値ファイルはテストクラスと同じパッケージに配置します - 更新系メソッドのテストの場合、テストメソッド終了後にコミットしたい場合は、@Rollback(false)と書けばOKです。
assert failでもコミットされるので注意してください。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
AppConfig.class,
EmployeeRepositoryImpl.class})
@TransactionConfiguration
@Transactional
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionDbUnitTestExecutionListener.class})
public class EmployeeRepositoryTest {
@Inject
EmployeeRepository sut;
/**
* 引数で受け取ったクラスのclassファイルがある物理ディレクトリパスを取得する
*
* @param clazz クラス
* @return 物理ディレクトリパス
*/
String resolvePhysicalDirectory(Class clazz) {
return new File(clazz.getResource("").getFile()).getPath() + File.separator;
}
@Test
@DatabaseSetup("EmployeeRepositoryTest_findAll_000_data.xml")
public void findAllのテスト() throws Exception {
// 期待値
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
String expedtedFile = "EmployeeRepositoryTest_findAll_000_expected.xml";
IDataSet expectedDataSet = builder.build(
new InputSource(resolvePhysicalDirectory(getClass()) + expedtedFile));
// テスト対象メソッド実行
List<Employee> actual = sut.findAll();
// DataSetに変換
IDataSet actualdDataSet = new BeanListConverter(actual).convert();
// assert
Assertion.assertEquals(expectedDataSet, actualdDataSet);
}
@Test
// @Rollback(false) // これを書くとコミットされる (assert failでもコミットされるので注意)
@DatabaseSetup("EmployeeRepositoryTest_findAll_000_data.xml")
@ExpectedDatabase(value = "EmployeeRepositoryTest_delete_000_expected.xml", table = "employee",
query = "select * from employee order by employee_id")
public void deleteのテスト() throws Exception {
// 削除対象を取得
Employee employee = sut.findById(4);
// テスト対象メソッド実行
sut.delete(employee);
}
}
ソースは ここ に置いてます。
SpringとDomaの組み合わせ、良いですよー。