山崎屋の技術メモ

IT業界で働く中でテクノロジーを愛するSIerのシステムエンジニア👨‍💻 | AndroidとWebアプリの二刀流🧙‍♂️ | コードの裏にあるストーリーを綴るブログ執筆者✍️ | 日々進化するデジタル世界で学び続ける探究者🚀 | #TechLover #CodeArtisan、気になること、メモしておきたいことを書いていきます。

DBUnit を使って Excel からデータを DB にロードする with Spring Boot

Junit を使用したユニットテスト(自動テスト)の基本を以前記事にしました。
www.shookuro.com

今回は DB も含めた自動テストのやり方をメモしておきたいと思います。

DB 絡みのユニットテストを行う場合、テストケースごとにテーブルのデータを準備したいことがあると思います。

DBUnit を使えばあらかじめ用意しておいたデータをテストメソッドごとにテーブルにインポートしてからテストを実行できます。

用意しておくデータは csv、xml、Excel 形式が利用できます。

参考サイト:
DBUnitの導入とxml,Excel,CSVのサンプルプログラム(DBUnit):技術空間

Junit や SpringBoot を利用した DB 接続については過去記事で紹介していますので参考にしてください。
【JUnit入門】ユニットテストの基本 - 山崎屋の技術メモ
Spring boot 入門 DB 接続する簡単な Web アプリを作成 - 山崎屋の技術メモ


今回は Spring Boot で作成したアプリで DBUnit を利用する方法。その際、 Excel からテストデータをインポートする方法をメモしておきたいと思います。

DB は postgresql 9.5。SpringBootは 2.1.2。DBUnit は 2.6.0。

テーブルの準備

次のようなテーブルを用意しました。

postgres=# \d member
                              テーブル "public.member"
  列  |          型           |                       修飾語
------+-----------------------+-----------------------------------------------------
 id   | integer               | not null default nextval('member_id_seq'::regclass)
 name | character varying(10) |
 age  | integer               |
インデックス:
    "member_pkey" PRIMARY KEY, btree (id)


テストデータの準備

 
Excelで次のようなブックを作成し「test-data.xlsx」というファイル名で保存しました。
f:id:yyama1556:20190120163129p:plain

シート名にはテーブル名を設定しておく必要があります。
 
このブックをプロジェクトのどこにおくかは悩みどころですが、小さいプロジェクトでしたらテストクラスと同じ場所でも良いのではないでしょうか。
 
 

pom.xml


DBUnit に直接関係ない設定もありますが、全量載せておきます。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.2.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>org.yyama</groupId>
	<artifactId>sample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>sample</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.dbunit</groupId>
			<artifactId>dbunit</artifactId>
			<version>2.6.0</version>
		</dependency>
		<dependency>
			<groupId>com.atomikos</groupId>
			<artifactId>transactions-osgi</artifactId>
			<version>4.0.6</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

ごらんのとおり DBUnit のバージョンは 2.6.0 です。

テスト対象クラス

簡単な DB 接続するクラスを用意しました。クラス名は Sample としました。

package org.yyama;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class Sample {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	// SQL
	private final String GET_NAME_OVER_20_AGE = "select * from member where age >= 20 order by id";

	public List<String> proc() {
		List<Map<String, Object>> list = jdbcTemplate.queryForList(GET_NAME_OVER_20_AGE);
		return list.stream().map(o -> (String) o.get("name")).collect(Collectors.toList());
	}
}

proc メソッドでテーブルを select し 20歳以上の人の名前をリストで返します。

最終的なプロジェクト構成です。
f:id:yyama1556:20190120163620p:plain

application.properties に DB 接続情報を記載しておくのも忘れないでください。以下サンプルです(環境によって変えてください)。

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=postgres


テストクラス

そしてテストクラスです。

package org.yyama;

import static org.junit.Assert.*;

import java.io.File;
import java.sql.Connection;
import java.util.List;

import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.excel.XlsDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class SampleTest_proc {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Autowired
	private Sample sample;

	private void tableSetUp(String excelName) throws Exception {
		// DBコネクション取得
		Connection conn = jdbcTemplate.getDataSource().getConnection();
		IDatabaseConnection dbconn = new DatabaseConnection(conn);

		// Excel用データセット作成
		File f = new File(ClassLoader.getSystemResource(excelName).getFile());
		IDataSet dataset = new XlsDataSet(f);

		// データの全削除
		DatabaseOperation.DELETE_ALL.execute(dbconn, dataset);

		// データの挿入
		DatabaseOperation.INSERT.execute(dbconn, dataset);
	}

	@Test
	public void _20歳以上の人の名前が返ること() throws Exception {
		// 準備
		tableSetUp("org/yyama/test_data.xlsx");

		// 実行
		List<String> list = sample.proc();

		// 検証
		assertEquals(2, list.size());
		assertEquals("fuga", list.get(0));
		assertEquals("piyo", list.get(1));
	}

}


tableSetUp メソッドで テーブルにテスト用データを投入しています。@Before メソッドを使用して、テストごとに実行される準備メソッドでも良いのですが、テストケースによってデータの Excel を変えたい場合は、このように Excel ファイル名を渡す共通メソッドを準備しておくと便利です。

処理についてはコメントにあるとおりなので解説は省略させていただきます。
 

テーブルのデータは戻らない

このままだと、テストのたびにテーブルのデータがテスト用に変わってしまうという問題があります

実行前にこんなデータがあったとして、、

postgres=# select * from member;
 id |  name   | age
----+---------+-----
 42 | yyama   |  25
 43 | ryotsu  |  38
 44 | akimoto |  24
 45 | bucho   |  55
(4 行)

実行後、勝手に変えられています。

postgres=# select * from member;
 id | name | age
----+------+-----
  1 | hoge |  19
  2 | fuga |  20
  3 | piyo |  21
(3 行)

これを解決する方法は別記事にしました。
【DbUnit】テスト完了時にテーブルデータを元に戻す方法 - 山崎屋の技術メモ

Excel を使用した DBUnit の使い方まとめ

今回は Spring Boot で作成されたアプリを DBUnit でテストする方法、その際 Excel にテストデータを準備しておいて自動でテーブルデータを変更しつつテスト実行する方法を紹介しました。

それでは!

Spring 関連記事へのリンク集つくりました。

テスト駆動開発

テスト駆動開発

  • 作者:Kent Beck
  • 発売日: 2017/10/14
  • メディア: 単行本(ソフトカバー)
JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

  • 作者:渡辺 修司
  • 発売日: 2012/11/21
  • メディア: 単行本(ソフトカバー)