Pitaliumを使用したテストにデータベースのリセット・アサーションを組み込む
Seleniumを使用してWebアプリをテストする場合、ブラウザからの操作によってデータベースに保存されている値が変化してしまい、次のテストケースに影響を与えてしまうことがあります。Pitaliumを使用したテストケースにおいて、データベースのリセットや、操作後のデータベースアサーションについて、簡単に解説します。
データベースのリセット・アサーション
DBUnit
Javaからデータベースを操作するにはいくつかの方法があります。JDBCDriverから操作する、JPAやMyBatisといったORマッパーを経由する、など様々です。今回は DbUnit という、JUnit拡張を使用します。
DbUnitはデータベーステストをするため、以下の便利な特徴があります。
- データベースのレコードをクリアする、用意したデータセットの中身を一括挿入する、などの初期操作やリセット操作
- データベースのレコードを、用意したデータセットを使用してデータベース単位、テーブル単位でアサーション
- データセットはエクセルファイルやCSV、XMLがサポートされ、データベースからのエクスポートも可能
業務用途では、特にエクセルファイルでの入出力がサポートされているのが便利です。
DBUnitを使用したテストケースの作成
概要
Pitaliumは複数ブラウザでの同時実行が可能なテストライブラリです。
静的ページへの複数アクセスでは特に問題となりませんが、データの登録・削除が発生するページでは
ブラウザがアクセスする順番によって表示される内容が変化してしまい、テストすることが難しくなります。
これを解消するには、テストするブラウザ毎にWebアプリを立ち上げる方法が挙げられます。
しかしPitaliumではブラウザ毎にアクセス先を変更することはサポートされていません。
ブラウザ毎の設定である capabilities.json に各ブラウザのアクセス先を記述することで
擬似的に、ブラウザ毎にそれぞれの接続先へアクセスすることが可能となります。
1.capabilities.jsonを変更する
まずは capabilities.json ファイルを修正します。
各ブラウザ毎に必要な情報は WebアプリのベースURLとデータベースの接続文字列 です。
SeleniumのCapabilitiesは、platformやbrowserName、version以外は自由に記述できますので、
例えば次の様にします。
種別 | capabilitiesのキー | サンプル値 |
---|---|---|
WebアプリのベースURL | pitalium.baseUrl | http://localhost:18080 |
データベースの接続文字列 | pitalium.db.connectionString | jdbc:h2:tcp://localhost//test_0;user=sa;password= |
{
"platform": "WINDOWS",
"browserName": "internet explorer",
"pitalium.db.connectionString": "jdbc:h2:tcp://localhost/~/test_0;user=sa;password=",
"pitalium.baseUrl": "http://localhost:18080/"
},
{
"platform": "WINDOWS",
"browserName": "chrome",
"pitalium.db.connectionString": "jdbc:h2:tcp://localhost/~/test_1;user=sa;password=",
"pitalium.baseUrl": "http://localhost:18081/"
}
]
2.テストに使用するデータを用意する
本サンプルではDbUnitを利用してテストケース毎にデータベースのレコードを初期化します。
データをエクセルで作成する際は、次のフォーマットとなります。
- エクセルのシート名をデータベースのテーブル名とする
- エクセルの1列目をデータベースのカラム名とする
- 2列目以降に実際のデータを記述する
本サンプルのデータベーススキーマイメージは次の通りです。
テーブル名 | カラム名 | 種別 | |
---|---|---|---|
USER | ユーザー情報のテーブル | ||
ID | INT | ユニークID | |
NAME | VARCHAR | ユーザー名 | |
AGE | INT | 年齢 | |
VARCHAR | メールアドレス | ||
ITEM | ユーザーが所有するアイテムのテーブル | ||
ID | INT | ユニークID | |
NAME | VARCHAR | アイテム名 | |
USER_ID | INT | 所有するユーザーのID |
初期化データのエクセルは以下の様になります。
ユーザーテーブル:
アイテムテーブル:
3.初期化データを挿入する
JUnitテストの@Beforeを使用して、テストケースの初期化処理のタイミングでデータベースのレコードをリセットします。
DataSetの作成
データベースを初期化するにはまずエクセルファイルからDataSetを生成します。
DataSet(IDataSetインターフェース)はDbUnitのクラスの1つで、テーブルデータの集合を現します。
先に述べた通りDbUnitはいくつかのデータ形式を扱うことができ、データ形式ごとにIDataSetの実装クラスが存在します。
エクセルをDataSetとするには org.dbunit.dataset.excel.XlsDataSet を使用します。
IDataSet dataSet = new XlsDataSet(in);
DatabaseConnectionの作成
次にDbUnitのデータベース接続クラスのDatabaseConnectionを生成します。
その際データベースの接続文字列が必要になりますので、capabilitiesから "pitalium.db.connectionString" をキーとして取得します。
Connection connection = DriverManager.getConnection(connectionString);
IDatabaseConnection databaseConnection = new DatabaseConnection(connection);
データベースの操作
最後に、データを挿入します。
DbUnitで標準に規定されたデータの操作方法を紹介します。
オペレーション | 概要 |
---|---|
INSERT | データセットのインサートを行う。 |
CLEAN_INSERT | データセットに存在するテーブルのデータが全て削除された後、データセットのデータがインサートされる。 |
UPDATE | データセットのデータのうち、既存データが存在するものがアップデートされる。 |
REFRESH | データセットのデータのうち、既存データが存在するものはアップデートされ、存在しないものはインサートされる。 |
DELETE | データセットで指定されたデータが削除される。 |
DELETE_ALL | データセットで指定されたテーブルの全データが削除される。 |
TRUNCATE_TABLE | データセットで指定されたテーブルの全データが削除される。 DELETE_ALLより高速だが、DROP-CREATEのため、データのロールバックが出来ない等の副作用がある。 |
今回はCLEAN_INSERTを使用し、全データを作り直します。
初期化操作全体
@Override
public void setUp() {
super.setUp();
// Capabilitiesに設定した接続先を取得する
String connectionString = (String) capabilities.getCapability("pitalium.db.connectionString");
// データベースの初期化を実行する
try {
Class.forName("org.h2.Driver");
} catch (Exception e) {
throw new RuntimeException(e);
}
try (InputStream in = getClass().getResourceAsStream("init_data.xlsx");
Connection connection = DriverManager.getConnection(connectionString)) {
// DataSetの生成
IDataSet dataSet = new XlsDataSet(in);
// DatabaseConnectionの生成
IDatabaseConnection databaseConnection = new DatabaseConnection(connection);
// 高速化用、バッチ処理設定
databaseConnection.getConnection().setAutoCommit(false);
databaseConnection.getConfig().setProperty(DatabaseConfig.FEATURE_BATCHED_STATEMENTS, true);
databaseConnection.getConfig().setProperty(DatabaseConfig.PROPERTY_BATCH_SIZE, 1000);
databaseConnection.getConfig().setProperty(DatabaseConfig.PROPERTY_FETCH_SIZE, 1000);
// データを全て削除し、挿入しなおす
DatabaseOperation.CLEAN_INSERT.execute(databaseConnection, dataSet);
databaseConnection.getConnection().commit();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
※※※
上記の様にAutoCommitを設定せず、またバッチ処理を有効化することで
大量データの処理が非常に高速になる場合があります。
4.WebアプリのURLを取得
Pitaliumにはテスト対象ページのベースURLを testAppConfig.json に記述すると
WebDriver#getへ渡すURLは、ベースURLからの相対パスで記述することができます。
しかし今回のテストではブラウザ毎にアクセス先を変更する必要があるため
ベースURLはPitaliumの自動補完を使用せず、Capabilitiesから取得します。
public void deleteUser() throws Exception {
String baseUrl = (String) capabilities.getCapability("pitalium.baseUrl");
driver.get(baseUrl + "user/");
...
}
5.操作後のデータベースアサーション
Webアプリを介して何らかの操作をした後、データベースに登録されているデータをDbUnitで確認します。
DbUnitはDataSetをDataSet単位、または内包するTable単位で比較することができます。
public void deleteUser() throws Exception {
...
// データベースの値をチェックする
String connectionString = (String) capabilities.getCapability("pitalium.db.connectionString");
try (InputStream in = getClass().getResourceAsStream("result_data.xlsx");
Connection connection = DriverManager.getConnection(connectionString)) {
// 正解データが含まれるエクセルファイルからDataSetを生成
IDataSet expectedSet = new XlsDataSet(in);
IDatabaseConnection databaseConnection = new DatabaseConnection(connection);
// データベースから比較元のDataSetを生成
IDataSet actualSet = databaseConnection.createDataSet();
// 全テーブルの値をまとめてチェックする
Assertion.assertEquals(expectedSet, actualSet);
}
}
DataSetまたはTable同士を org.dbunit.Assertion#assertEquals メソッドを使用して比較します。
DataSet、Tableに対してJUnitやhamcrestのassertEqualsを使用して比較するとテストは必ず失敗してしまいますので注意してください。
テストに使用するWebアプリケーション
概要
今回のテストサンプルは静的ページではなくデータの削除を含むWebアプリです。
フレームワークとして Spring Boot を、データベースとして H2 Database を使用しています。
このWebアプリは以下の機能を持ちます。
- データベースに登録されているユーザーを一覧表示する
- 選択されたユーザーをデータベースから削除する
データベースを起動する
Webアプリを立ち上げる前に、以下の手順の通りデータベースを起動します。
- h2 のウェブサイトから Platform Independent のzipアーカイブをダウンロードします。
本記事執筆時点での最新バージョンは 1.4.193 (2016-10-31) です。 - ダウンロードしたzipアーカイブを解凍します。
- 解凍したディレクトリの中にある h2/bin/h2.bat をダブルクリックし、h2データベースを起動します。
- Webブラウザが開き、h2コンソールのログイン画面が表示されます。
- 上記設定でログインを実行するとh2データベースに接続できます。
なおWindows環境では、 jdbc:h2:tcp://localhost//test_0 の接続文字列で接続すると
C:\Users\{ユーザー名}\test_0.mv.db というデータファイルが生成されます。
Webアプリを起動する
Webアプリを以下の手順で起動します。
- Webアプリのソースコードをこちら(Github:TODO)からダウンロードします。
または以下のコマンドを使用して、ソースコードをクローンします。
git clone https://github.com/todo - Antを使用してパッケージを生成し、実行します。
ant -lib ant-lib\spring-boot-antlib.jar build.xml
java -jar build\pitalium-sample-db-web.jar
Webアプリのデータベース参照先、起動ポートを変更する
今回のWebアプリは同時テストするブラウザの個数に合わせて複数起動しなければなりません。
そこでSpringのプロファイル機能を利用して、データベースの参照先およびWebの待ちうけポートを変更します。
初期状態ではプロファイルは2つ設定してあり、最大2ブラウザの同時実行が可能です。
3ブラウザ以上を同時に実行するにはプロファイルを編集し、新規プロファイルを追加します。
編集は src/main/resources/config/application.yml を修正します。
profiles:
active: pita-test-00
datasource:
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: update
---
spring:
profiles: pita-test-00
datasource:
url: jdbc:h2:tcp://localhost/~/test_0
server:
port: 18080
---
spring:
profiles: pita-test-01
datasource:
url: jdbc:h2:tcp://localhost/~/test_1
server:
port: 18081
クローンした時点ではapplication.ymlは上記の通りのなっています。
各プロファイルはファイル内の "---" によって分けられ、ファイル内の上の部分が全プロファイル共通の設定、
真ん中が1つ目のプロファイル、下が2つ目プロファイルとなっています。
プロファイル1 | プロファイル2 | |
---|---|---|
プロファイル名 | pita-test-00 | pita-test-01 |
デフォルト | ○ | |
DB接続先 | jdbc:h2:tcp://localhost//test_0 | jdbc:h2:tcp://localhost//test_1 |
起動ポート | 18080 | 18081 |
たとえば3つ目のプロファイルを追加するには、application.ymlに以下を追記します。
spring:
profiles: pita-test-02
datasource:
url: jdbc:h2:tcp://localhost/~/test_2
server:
port: 18082
特定のプロファイルで起動するには、起動引数にプロファイル名を追加します。