Dao層のテスト実践


1.Daoユニットテストの問題
Dao層の主な仕事はデータベースアクセスであり、非常に重要なモジュールである.SQLの正確な実行を保証するために、ユニットテストが必要です.しかし、これまでDao層のユニットテストは難しかった.主にいくつかの問題があったからだ.
1、ユニットテストは隔離された環境コードを実行しなければならないが、隔離データベースは非常に困難であり、この考えを放棄せざるを得ない.したがって、Daoレイヤはデータベースと直接付き合う必要がありますが、ユニットテストでは繰り返した動作の結果が一致する必要がありますが、外部データベース環境の問題でテスト環境が安定していません.
2、現段階のDao層は一般的にSpringの容器を利用してDaoオブジェクトを組み立て、いくつかのSupportオブジェクトを補佐する.その結果Spring容器がなく、Daoをテストできませんでした.
3、各テストの前に、データベースは安定した既知の状態にある必要があります.これはデータの準備が必要ですが、ユニットテストのデータは手動でデータベースに挿入する場合、作業量が大きすぎます.
4、テスト用例には断言が必要です.断言によってデータが挿入されたかどうか、各フィールドが同じかどうかを判断する必要があります.これは補助ツールがなければ、手作業で断言しなければなりません.仕事量は受け入れられません.
2.ソリューション
以上の問題を解決するために、Spring、Dbunitなどを統合するためにUnitilsを選択し、Dao層のユニットテストを完了し、Mavenエンジニアリングと組み合わせて構成を完了しました.
Unitilsの使用は簡単で、次の例で説明します.
Daoのコードはとても簡単で、1人のユーザーの口座の情報を検索して更新します
public class AccountDao extends JdbcDaoSupport {

    public Account getAccount(String accountId) {
        List<Account> list = null;

        list = getJdbcTemplate().query("select account_id,balance from tb_account where account_id=?",
                new Object[] { accountId }, new RowMapper<Account>() {

                    @Override
                    public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
                        Account acc = new Account();
                        acc.setAccountId(rs.getString("account_id"));
                        acc.setBalance(rs.getInt("balance"));
                        return acc;
                    }
                });
        if (list.size() > 0) {
            return list.get(0);
        } else {
            return null;
        }
    }

    public int updateAccount(String accountId, int balance) {
        int ret = getJdbcTemplate().update("update tb_account set balance = ? where account_id =?",
                new Object[] { balance, accountId });
        return ret;
    }
}

一、MavenのPOMファイルの修正
DaoエンジニアリングでのPOMファイルは以下のように追加されています.
		<dependency>
			<groupId>org.unitils</groupId>
			<artifactId>unitils-dbunit</artifactId>
			<version>${unitils.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.unitils</groupId>
			<artifactId>unitils-spring</artifactId>
			<version>${unitils.version}</version>
			<scope>test</scope>
		</dependency>

unitils.バージョンの最新バージョンは3.3です
二、ユニットの環境構成
Unitilsの起動には、プロファイルunitilsが必要です.properties、このファイルはデフォルトでclasspathの下に置く必要があります.私たちは一般的にtest/resources/unitilsです.propertiesファイル.ファイルの内容は次のとおりです.
database.driverClassName=com.mysql.jdbc.Driver
database.url=jdbc:mysql://192.168.100.242:3306/test
database.userName=mantis
database.password=mantispw
database.schemaNames=test
database.dialect=mysql
DatabaseModule.Transactional.value.default=rollback

database.driverClassNameは、データベースをテストするJdbcドライバです.
database.urlはテストデータベースの接続列です
database.userNameはテストデータベースのユーザー名です
database.schemaNamesは、データベースをテストするschemaです.mysqlは不要です.Oracleは必須です.
database.dialectはデータベースタイプとして記入し、mysql、oracle、derbyなどの値をとる
DatabaseModule.Transactional.value.defaultとは、ユニットテストによるデータベースの変更のトランザクションポリシーを指し、rollback、disable、commitなどの選択があり、一般的にrollbackをロールバックすることを選択します.
三、Springの集積
UnitilsはSpringの統合機能を提供し、ユニットテストでSpringに私たちのDaoを組み立てることができ、依存するData Sourceなどを自動的に注入することができます.
Spring統合には、いくつかの事前条件が必要です.
1.Property解析、DataSource、トランザクション管理などを含むDao依存Spring構成は、主にMavenエンジニアリングのtest/resources/testapplication/appContext-commonに配置する.xmlで.
<bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
		<property name="ignoreResourceNotFound" value="false" />
		<property name="ignoreUnresolvablePlaceholders" value="true" />
		<property name="locations">
			<list>
				<value>classpath:testapplication/config.properties</value>
			</list>
		</property>
	</bean>

	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName">
			<value>${jdbc.driverClassName}</value>
		</property>
		<property name="url">
			<value>${jdbc.url}</value>
		</property>
		<property name="username">
			<value>${jdbc.username}</value>
		</property>
		<property name="password">
			<value>${jdbc.password}</value>
		</property>
		<property name="maxActive">
			<value>${jdbc.maxActive}</value>
		</property>
		<property name="maxIdle">
			<value>${jdbc.maxIdle}</value>
		</property>
		<property name="initialSize">
			<value>${jdbc.maxIdle}</value>
		</property>
		<property name="maxWait">
			<value>18000</value>
		</property>
		<property name="defaultAutoCommit">
			<value>false</value>
		</property>
	</bean>
	<!--        ,       -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

ユニットテストの基礎Spring構成を単独で抽出したのは、ここに注入されたデータソースとトランザクションマネージャが、最も簡単な単一データベーストランザクションを使用して、ユニットテストの環境を簡素化しているからです.実際の開発におけるマルチデータ・ソース・トランザクションの問題が結果に影響を及ぼすことを回避します.
2、UnitilsのSpringを使用して置換機能を起動し、Springの正常なDataSourceをUnitils自身のDataSourceに変更する.このような利点は、データ準備の操作とビジネスsqlが1つのトランザクションで行われ、データベースに影響を与えないように、一緒にロールバックするのが便利であることです.代替データソースもSpringプロファイルで、test/resources/testapplication/testDataSourceに配置されます.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-3.1.xsd
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-3.1.xsd">
	<bean id="dataSource" class="org.unitils.database.UnitilsDataSourceFactoryBean" />
</beans>

三、テストデータの準備
dbunitのxml形式でテストデータを準備し、ExportDataというオブジェクトを実行することでテストライブラリが表示されたデータを導き、コマンドラインにエクスポートするテーブル名を入力することで、現在のテストデータベースの既存データをxmlにエクスポートできます
テストのxmlファイルは、test/resourcesの下とテストのコードが同じpackageにデフォルトで配置されます.たとえば、test/resources/com/xxx/dao/の下です.
四、ユニットテスト用例の作成
ユニットテストの例はUnitilsJUnit 3というベースクラスを継承する必要があり、その名の通りこのテストキットはJunit 3に依存している.Unitilsはまた、UnitilsJUnit 4のベースクラスも提供しています.次に、テストサンプルコードを示します.
public class AccountDaoTest extends UnitilsJUnit3 {

    @SpringApplicationContext({ "classpath:testapplication/appContext-common.xml",
            "classpath:testapplication/testDatasource.xml", "classpath:META-INF/spring/applicationContext-*.xml" })
    protected ApplicationContext applicationContext;

    @SpringBeanByType
    private AccountDao accountDao;

    @DataSet("ACCOUNT.xml")
    public void testGetAccount() {
        Account account = accountDao.getAccount("S31993k");
        System.out.println(JSON.toJSON(account));
        assertEquals(100, account.getBalance());
    }

    @DataSet("ACCOUNT.xml")
    public void testGetAccountNull() {
        Account account = accountDao.getAccount("23");
        assertEquals(null, account);
    }
    @DataSet("ACCOUNT.xml")
    @ExpectedDataSet("ACCOUNT_NEW.xml")
    public void testUpdateAccount() {
        accountDao.updateAccount("S31993k", 35);

    }

}

protected ApplicationContext applicationContext;
Springコンテキストにロードされた変数です.上の@SpringApplicationContextは、ロードするSpringプロファイルを変数に指定し、1つのString配列とワイルドカードで複数の構成をロードできます.ここではcommonとテストデータソースのtestDatasourceを見ることができます.xmlはすべてロードされました.1つのプロジェクトで共通のテストベースクラスを抽象化し、Springのコンテキストをベースクラスに保存できます.
private AccountDao accountDao;
テストするターゲットオブジェクトです.ここでは@SpringBeanByTypeの寸法を前に付ける必要があります.これにより、Unitilsは自動的にタイプに応じてターゲットオブジェクトをSpringコンテキストから取り出し、テストコードに注入します.
public void testUpdateAccount ()
挿入をテストする方法です.@DataSet(「ACCOUNT.xml」)
挿入前のプリフォームデータを指し、UnitilsとDbunitはこのメソッドを実行する前にEMPTY_TABLE.xmlのデータはデータベースにインポートされ、次にサンプルデータのxmlを示します.実行する前に、Unitilsはテーブルを空にし、指定したテストデータを挿入します.注意:トランザクションが最後にロールバックされるため、空のアクションはコミットされません.データの損失を心配する必要はありません.

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
  <tb_account account_id="S31993k" balance="100"/>
 </dataset>

@ExpectedDataSet("ACCOUNT_NEW.xml")
テスト例を実行した後の期待データのために、Unitilsは実際の結果と期待値を比較し、一致しているかどうかを確認します.一致しない場合は、放出テストに失敗します.ACCOUNT_NEW.xmlの内容は以下の通りです.
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
  <tb_account account_id="S31993k" balance="35"/>
 </dataset>

特に、dbunitは、多くのプライマリ・キーが自動的に生成されるため、比較時にプライマリ・キーの比較を無視します.xmlにプライマリ・キーの属性値を付ける必要はありません
public void testGetAccount()は、すでに存在するアカウントをクエリーします.最後に、クエリ番号が「S 31993 k」であるか否かを断言する.注意:DBUnitは、使用例メソッドを実行するたびに関連するデータテーブルを再初期化するので、前のテスト使用例操作が現在の使用例結果に影響を及ぼす心配はありません.
public void testGetAccountNull
これは存在しないアカウント情報を検索しようとしたもので、最後にDaoが返すべきはNullで、ここで断言で判断しました.
3.経験まとめ
UnitilsはDbunitとSpringを統合することで、ユニットテストの作業量が小さく、効果的です.
次はDaoユニットのテストをした経験です.
1、ユニットテストのデータベースと他のテストのデータベースをできるだけ分離し、相互影響を避ける.各開発者が独自のユニットテストライブラリを持っている場合が望ましい.
2、Dbunitが必要とするデータセットは予めデータベースから生成しておき、後のテストではこれらのデータセットを再利用することができる.データセットはできるだけ小さく、テストに影響を与えるデータだけが含まれていることを保証します.
3、データベースの外部キーはユニットテストの障害で、外部キーを取り除くことをお勧めします.データの整合性はアプリケーションによって保証されるべきです.
4、断言がなければユニットテストとは言えない.ユニットテストは必ず自動化し、人の目で判断するのではなく、自動化しなければならない.
5、Daoのユニットテストが複数のテーブルに関連する場合(この場合は珍しい)、1つのxmlに複数のテーブルのデータを配置することができ、Dbunitは自動的に異なるテーブルにインポートされたことを認識します.