11
11

More than 5 years have passed since last update.

Java + Spring によるテストデータ管理

Last updated at Posted at 2014-08-07

Java + Spring でテストデータを効率よく管理する方式を模索する

この記事はゆっくり完成させるつもりでいる。

最終目標

  • テスト対象はDBアクセス用のORマッパ(主にSQL)とする
  • テスト実行の流れは以下の通りとする
    • JUnitでテスト用データを投入する
    • ORマッパがクエリを実行し結果をbeanで返す
    • JUnitのAssertで結果を検証する
  • テストデータはテストメソッドと対でフォルダ内で管理する
  • テストデータはExcelかCSVで作成する
  • テーブル作成スクリプトも一緒に管理し、設計書管理による乖離を解消する

以下のサンプルでは段階的に利用するパッケージを増やし、各パッケージの理解を深める。そうすることで利用するパッケージの差し替えを可能にすることを狙っている。

はじめに

DB を用意する。本記事ではどの環境でも動くことを前提とするためにJava DBを使ってサンプルを作成する

JavaDBSample.java
import java.sql.*;

class JavaDBSample {
    public static void main(String[] args) throws SQLException {
        Connection conn = DriverManager.getConnection(
            "jdbc:derby:/tmp/javadb/sample;create=true");

        conn.close();
    }
}

以下により、/tmp/javadb/sample ディレクトリが作成されその配下にDB関連のファイルができる

javac JavaDBSample.java
java -classpath "$JAVA_HOME/db/lib/derby.jar;." JavaDBSample

SQL発行までを試す

JavaDBSample.java
import java.sql.*;

class JavaDBSample {
    public static void main(String[] args) throws SQLException {
        Connection conn = DriverManager.getConnection(
            "jdbc:derby:/tmp/javadb/sample;create=true");

        try {
            drop(conn, "sample");

            Statement stat = conn.createStatement();
            stat.execute("create table sample (id numeric(3), str varchar(5))");
            stat.close();

            conn.setAutoCommit(false);
            stat = conn.createStatement();
            stat.execute("insert into sample values (1, 'a')");
            stat.close();

            stat = conn.createStatement();
            ResultSet rs = stat.executeQuery("select * from sample");
            while (rs.next()) {
              System.out.println(rs.getInt("ID") + ", " + rs.getString("STR"));
            }
            rs.close();
            stat.close();
            conn.commit();

            conn.close();
        }
        finally {
            try {
                shutdown();
            }
            catch (SQLException e) {
                // ignore
            }
        }
    }

    public static void drop(Connection conn, String table) {
        try {
            Statement stat = conn.createStatement();
            stat.execute("drop table " + table);
            stat.close();
        }
        catch (SQLException e) {
            // ignore
        }
    }
    public static void shutdown() throws SQLException {
        DriverManager.getConnection("jdbc:derby:;shutdown=true");
    }

}

コンパイルする

javac JavaDBSample.java

実行する

java -classpath "$JAVA_HOME/db/lib/derby.jar;." JavaDBSample
1, a

Gradle でビルドする

以降、依存ライブラリの解決に gradle を利用するので、gradleをインストールしておく

Gradle

Gradle は現在 2.0 になっているが、ここでは自分の環境にインストール済みの 1.12 で試した結果を記載する

gradle -version
------------------------------------------------------------
Gradle 1.12
------------------------------------------------------------

Build time:   2014-04-29 09:24:31 UTC
Build number: none
Revision:     a831fa866d46cbee94e61a09af15f9dd95987421

Groovy:       1.8.6
Ant:          Apache Ant(TM) version 1.9.3 compiled on December 23 2013
Ivy:          2.2.0
JVM:          1.7.0_40 (Oracle Corporation 24.0-b56)
OS:           Windows 7 6.1 x86

Gradleで先のソースをビルドするため、以下のファイルを作成する。

build.gradle
apply plugin: 'java'

JavaDBSample.java を以下のパスに移動する
(Gradleではソースツリーの構成が定められている)

src/main/java/JavaDBSample.java

前のサンプルで作成していた余分な .class を削除しておく

rm *.class

コンパイルする

gradle build

実行する。混乱を避けるために前に作ったDBは一旦消しておく

rm -rf /tmp/javadb/sample

ビルドされたclass ファイルは build/classes/main 配下に作成されるためクラスパスを変更していることに注意。

java -classpath "$JAVA_HOME/db/lib/derby.jar;build/classes/main" JavaDBSample
1, a

Spring を使う

参考:Spring環境におけるDBアクセス

必要な spring の jar は gradle にダウンロードさせる

build.gradle
apply plugin: 'java'

repositories {
    mavenCentral()
}

ext {
    springVersion = '4.0.5.RELEASE'
}

dependencies {
    compile "org.springframework:spring-beans:$springVersion"
    compile "org.springframework:spring-context:$springVersion"
    compile "org.springframework:spring-context-support:$springVersion"
    compile "org.springframework:spring-tx:$springVersion"
    compile "org.springframework:spring-jdbc:$springVersion"
}

SpringのapplicationContext.xmlを以下の通り作成する。
今度は、一般的なサンプルに合わせ、パッケージを付けることにする。
パッケージ名は、jp.sample とした。

DBアクセス周りは慣習(?)に合わせ、META-INF/datasource-context.xml
に記述し、importしている。

src/main/resources/applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  <context:annotation-config />
  <context:component-scan base-package="jp.sample" />
  <import resource="META-INF/datasource-context.xml" />
</beans>

META-INF/datasource-context.xml
の設定値は、JavaDBを使うため、以下の通りとした。

設定項目
driverClassName org.apache.derby.jdbc.EmbeddedDriver
url jdbc:derby:/tmp/javadb/sample;create=true
jdbcTemplate の databaseProductName Derby

databaseProductName はなくても良いかもしれない。
(databaseProductName を "Apache Derby" と書いていたが、実際は "Derby" だった。sql-error-codes.xmlを参照)
username, password は、一般的なDBに合わせ設定しているが、ここで作成しているJavaDBでは実際は関係ない。

src/main/resources/META-INF/datasource-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" />
    <property name="url" value="jdbc:derby:/tmp/javadb/sample;create=true" />
    <property name="username" value="user" />
    <property name="password" value="pass" />
  </bean>

  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>

  <tx:annotation-driven transaction-manager="transactionManager" />

  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
    <property name="databaseProductName" value="Apache Derby" />
  </bean>
</beans>

jp.sample.JavaDBSample2 クラスを作成する

src/main/java/jp/sample/JavaDBSample2.java
package jp.sample;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class JavaDBSample2 {

  public static void main(String[] args) {
    try (ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml")) {
      JavaDBSample2 sample = context.getBean(JavaDBSample2.class);
      sample.execute();
    }
  }

  @Autowired
  JdbcTemplate jdbcTemplate;

  @Transactional
  public void execute() {
    // do some work
  }
}

コンパイルする

gradle build

class ファイルが build/classes/main/jp/sample/ に作成される
実行にあたっては、ダウンロードしたjarファイルにクラスパスを通すなど
面倒くさいので実行もgradleで行うことにする。

build.gradle を以下のとおり書き換える

なお、$JAVA_HOME/db/lib/derby.jar にクラスパスを渡すのも
面倒くさかったので、javaDB である derby.jar もダウンロード
するように変更した。

build.gradle
apply plugin: 'application'

mainClassName = 'jp.sample.JavaDBSample2'

repositories {
    mavenCentral()
}

ext {
    springVersion = '4.0.5.RELEASE'
}

dependencies {
    compile "org.springframework:spring-beans:$springVersion"
    compile "org.springframework:spring-context:$springVersion"
    compile "org.springframework:spring-context-support:$springVersion"
    compile "org.springframework:spring-tx:$springVersion"
    compile "org.springframework:spring-jdbc:$springVersion"

    compile 'org.apache.derby:derby:10.14.+' // 以下の2019/5/30 追記参照
}

※ 2019/5/30 追記: javaDB(Apache Derby)のEmbeddedDriverは、直近のversion 10.15.1.3 で、derbytools.jarに移動した。またこのバージョンではJDK9を必要とする。本記事ではJDK8を対象としたいため10.14を指定することにした (Apache Derby 10.15.1.3 Release)

実行する。前に作ったDBは一旦消す

rm -rf /tmp/javadb/sample

gradle run

クエリを実行する
JavaDBSample と同じようなcreate table, insert, select をJdbcTemplateで実行する

ID は、BigDecimal になってしまった。RowMapperを使うようにしないとダメなのか?

src/main/java/jp/sample/JavaDBSample2.java
package jp.sample;

import java.sql.ResultSet;
import java.sql.SQLException;

import java.util.List;
import java.util.Map;
import java.math.BigDecimal;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class JavaDBSample2 {

  public static void main(String[] args) {
    try (ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml")) {
      JavaDBSample2 sample = context.getBean(JavaDBSample2.class);
      sample.execute();
    }
  }

  @Autowired
  JdbcTemplate jdbcTemplate;

  @Transactional
  public void execute() {
      jdbcTemplate.execute("create table sample2 (id numeric(3), str varchar(5))");
      jdbcTemplate.execute("insert into sample2 values (2, 'b')");
      List<Map<String,Object>> rows = jdbcTemplate.queryForList("select * from sample2");

      for (Map row : rows) {
          System.out.println((BigDecimal)row.get("ID") + ", " + (String)row.get("STR"));
      }
  }
}

JUnitで動作確認をする

この記事の目的はテストなので、JUnitでテストを作成し、結果の正しさ
(ここでは、レコード(2, 'b')が作られること)を確認してみる

Spring でJUnitのテストを書くには、それ用のライブラリ(spring-test)が必要になるようなので、JUnit と一緒にGradleで取ってくることにする。

@RunWithと@ContextConfigurationでspring用のjunitを作成する

また、実行は必要なくなるので、plugin は application から java に戻すことにする

build.gradle
apply plugin: 'java'

repositories {
    mavenCentral()
}

ext {
    springVersion = '4.0.5.RELEASE'
}

dependencies {
    compile "org.springframework:spring-beans:$springVersion"
    compile "org.springframework:spring-context:$springVersion"
    compile "org.springframework:spring-context-support:$springVersion"
    compile "org.springframework:spring-tx:$springVersion"
    compile "org.springframework:spring-jdbc:$springVersion"

    compile 'org.apache.derby:derby:10.+'

    testCompile 'junit:junit:4.+'
    testCompile "org.springframework:spring-test:$springVersion"
}

また、テストクラスを以下のように作成する

src/test/java/jp/sample/JavaDBSample2Test.java
package jp.sample;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.ContextConfiguration;
import org.junit.runner.RunWith;

import org.junit.*;
import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class JavaDBSample2Test {

    @Test
    public void test() {
        assertTrue(true);
    }
}
gradle check

なお、コンパイルエラーではなくテストが実行され失敗した場合には、テストの結果が以下に書かれるので参照する

build/reports/tests/index.html

次に、ライブラリの不足等ないことを確認したらちゃんとテストを書いてみる

11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11