JUnit3.8 | JUnit4.x | |
---|---|---|
ライブラリのインポート | TestCaseクラスをインポートする。 | Assertクラスの各メソッドをstaticインポートする。 Testアノテーション等をインポートする。 |
テストクラスの定義 | TestCaseを継承する必要がある。 | 特になし。 |
テストメソッドの定義 | 「public void test~() 」testで始まる名前で、public voidで引数なしのメソッドが実行対象。 | 「@Test public void ~() 」@Testアノテーションを付けたpublic voidで引数なしのメソッドが実行対象。 |
テストメソッド実行前後 | setUp() tearDown()が 各テストメソッドの実行前後に呼ばれる。 | @Before @Afterを付けたメソッドが、 各テストメソッドの実行前後に呼ばれる。 |
全テストの実行前後 | なし。 | @BeforeClass @AfterClassを付けたメソッドが、 全テストの実行前後に呼ばれる。 |
等値の確認 | TestCase#assertEquals() を使用。(TestCaseを継承しているので「 assertEquals() 」で使用可能) | Assert.assertEquals() を使用。(static importすれば「 assertEquals() 」で使用可能) |
例外発生の確認 | Exceptionをcatchし、catchできなかったらfail()を呼ぶ。 | @Testアノテーションの 引数expectedに発生すべき例外クラスを指定。 |
実装例 | JUnit3.8 | JUnit4.x |
import junit.framework.TestCase; public class SampleTest extends TestCase { public void testNormal() { 対象クラス obj = new 対象クラス(); int r = obj.試験対象メソッド(); assertEquals(123, r); assertEquals(456, obj.get内部状態()); }
public void testException() {
try {
試験対象実行();
fail("例外が発生するはず");
} catch(発生すべき例外 e) {
// success
assertEquals("文言", e.getMessage());
}
//試験の続き~
}
} | import static org.junit.Assert.*; import org.junit.Test; public class SampleTest { @Test public void normal() { 対象クラス obj = new 対象クラス(); int r = obj.試験対象メソッド(); assertEquals(123, r); assertEquals(456, obj.get内部状態()); } @Test(expected=発生すべき例外.class)
public void testException() {
試験対象実行();
//文言の確認は無理そう?
//別の試験を続けるのは無理そう?
}
} |
2013年5月29日水曜日
JUnit DBUnit - 5
2013年5月28日火曜日
JUnit DBUnit - 4
DbUnitでexcelファイルを読み込み、DBへ登録する初期値やテスト結果の期待値を取得します。それにはorg.dbunit.dataset.excel.XlsDataSetを使います。
このXlsDataSetはコンストラクタでjava.io.Fileかjava.io,InputStreamを渡します。
DbUnitを拡張して独自にテストフレームワークを作成する
クラスローダを利用
ClassLoader#getResourceAsStream()を呼び出し、InputStreamを取得
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("excelファイルのフルパス");
このXlsDataSetはコンストラクタでjava.io.Fileかjava.io,InputStreamを渡します。
DbUnitを拡張して独自にテストフレームワークを作成する
クラスローダを利用
ClassLoader#getResourceAsStream()を呼び出し、InputStreamを取得
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("excelファイルのフルパス");
JUnit DBUnit - 3
DaoBaseTestCaseは、テストデータExcelファイル(xxx_prepare.xls)からIDataSetオブジェクトを生成し、テストデータとして使用します。テストの実行前にこのテストデータが自動的にデータベースに反映されることになります。
事前処理の元データを退避し、テストデータを投入する機能や事後処理の元データを復元する機能、またはデータベースに登録されているデータを取得する機能などを、あらかじめ基底クラスに用意しておけば、基底クラスを継承して作成する各テストクラス作業が大変楽になります。
getConnectionは、DBコネクションを取得するメソッドです。getActualTableは、データベースに登録されているデータを取得するメソッドとなります。
getExpectedTableは、テスト結果として期待されるデータを取得するメソッドです。getTableRowCountは、データベースに登録されているデータ件数を取得するメソッドとなります。
assertTableは、期待値とデータベースに登録されているデータの比較をするメソッドです。isSkipColumnは、assertTableメソッド実行時に、比較をスキップするカラム名を指定するメソッドとなります。
assertEntityは、期待値とEntityデータの比較をするメソッドです。getTestFileNameは、テストデータExcelファイルの名前を取得するメソッドとなります。
getExpectedTableは、期待値データを取得するメソッドです。getSubClassは、サブクラスの実行時クラスを取得するメソッドとなります。
テストケース作成手順
①まず、テストクラスには基底クラスのDaoBaseTestCaseを継承させます(リスト1)。
②次に、DaoBaseTestCaseで宣言されている抽象メソッド「getTestFileName」「getTestFileName」「getSubClass」を実装します(リスト2)。
getTestFileNameは、テストデータExcelファイルの名称を指定します。getExpectedTableは、テスト結果として期待されるデータを取得します。getSubClassは、自分自身の実行時クラスを指定します。
登録(更新)系のテストで、期待値とデータベースに登録されているデータの比較をする際、期待値を予測できないカラム(例:「更新日時」など現在日時が設定されるカラム)が存在する場合は、DaoBaseTestCaseクラスのisSkipColumnメソッドをオーバーライドし、比較しない(スキップする)カラムを指定します(リスト3)。
スキップするカラムが存在しない場合、isSkipColumnメソッドはオーバーライドする必要はありません。
最後にテストアセットを作成し、テストコードを記述します。
登録(更新)系は、登録(更新)後のテーブルの値とExcelシートのデータを比較します(リスト4)。検索系は検索結果が正しいかどうかをEntityとExcelシートで比較します(リスト5)。
package dbunit_sample;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.Date;
import junit.framework.TestCase;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.Column;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.ReplacementDataSet;
import org.dbunit.dataset.excel.XlsDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.operation.DatabaseOperation;
/**
* DAOテストの基底クラス.
*/
public abstract class DaoBaseTestCase extends TestCase {
/**
* 全テーブル名.
*/
protected static final String[] TABLE_NAME_ALL;
/**
* テーブル数.
*/
private static final int TABLE_SIZE = 1;
/**
* テーブル名.
*/
private static final String TABLE_NAME = "syohin";
/**
* 元データ退避用一時ファイル.
*/
private File file;
/**
* データベースコネクション.
*/
private IDatabaseConnection connection;
/**
* テーブル名を取得する.
*/
static {
TABLE_NAME_ALL = new String[TABLE_SIZE];
for (int i = 0; i < TABLE_NAME_ALL.length; i++) {
String tableName = TABLE_NAME + (i + 1);
if (null == tableName) {
break;
}
TABLE_NAME_ALL[i] = tableName;
}
}
/**
* コンストラクタ.
*
* @param arg0 パラメータ
*/
public DaoBaseTestCase(String arg0) {
super(arg0);
}
/**
* 事前処理.
* 元データを退避し、テストデータを投入する。
*
* @throws Exception 事前処理中にエラーが発生した
*/
protected void setUp() throws Exception {
super.setUp();
try {
connection = this.getConnection();
QueryDataSet queryDataSet = new QueryDataSet(connection);
for (int i = 0; i < TABLE_NAME_ALL.length; i++) {
queryDataSet.addTable(TABLE_NAME_ALL[i]);
}
this.file = File.createTempFile("tmp", ".xml");
FlatXmlDataSet.write(queryDataSet, new FileOutputStream(this.file));
DatabaseOperation.CLEAN_INSERT.execute(connection, this
.getDataSet(getTestFileName()));
} catch (Exception e) {
if (connection != null) {
connection.close();
}
throw e;
}
}
/**
* 事後処理.
* 元データを復元する。
*
* @throws Exception 事後処理中にエラーが発生した
*/
protected void tearDown() throws Exception {
super.tearDown();
try {
DatabaseOperation.CLEAN_INSERT.execute(connection,
new FlatXmlDataSet(this.file));
} catch (Exception e) {
throw e;
} finally {
if (connection != null) {
connection.close();
}
}
}
/**
* DBコネクションを取得する.
*
* @return DBコネクション
*/
protected IDatabaseConnection getConnection() {
return ConnectionManager.getConnection();
}
/**
* データベースに登録されているデータを取得する.
*
* @param tableName テーブル名
* @return データベースに登録されているデータ
* @throws SQLException データベースに登録されているデータの
* 取得中にエラーが発生した
*/
protected ITable getActualTable(String tableName) throws Exception {
IDatabaseConnection connection = null;
try {
connection = getConnection();
return connection.createDataSet().getTable(tableName);
} catch (Exception e) {
throw e;
} finally {
if (connection != null) {
connection.close();
}
}
}
/**
* テスト結果として期待されるデータを取得する.
*
* @param tableName テーブル名
* @param fileName テストファイル名
* @return テスト結果として期待されるデータ
* @throws Exception データの取得中にエラーが発生した
*/
protected ITable getExpectedTable(String tableName, String fileName)
throws Exception {
return this.getDataSet(fileName).getTable(tableName);
}
/**
* データベースに登録されているデータ件数を取得する.
*
* @param tableName テーブル名
* @return データ件数
* @throws Exception データ件数の取得中にエラーが発生した
*/
protected int getTableRowCount(String tableName) throws Exception {
IDatabaseConnection connection = null;
try {
connection = getConnection();
return connection.createDataSet().getTable(tableName).getRowCount();
} finally {
if (connection != null) {
connection.close();
}
}
}
/**
* 期待値とデータベースに登録されているデータの比較をする.
*
* @param expectedTable 期待値データ
* @param actualTable データベースに登録されているデータ
* @throws DataSetException データの比較中にエラーが発生した
*/
protected void assertEquals(ITable expectedTable, ITable actualTable)
throws DataSetException {
for (int i = 0; i < expectedTable.getRowCount(); i++) {
for (int j = 0; j < expectedTable.getTableMetaData().getColumns().length; j++) {
Column[] columns = expectedTable.getTableMetaData()
.getColumns();
String columnName = columns[j].getColumnName();
if (this.isSkipColumn(columnName)) {
continue;
}
assertEquals("Line:" + (i + 1) + " Column:" + columnName,
convertData(expectedTable.getValue(i, columnName)),
convertData(actualTable.getValue(i, columnName)));
}
}
}
/**
* assertTableメソッド実行時、比較をスキップするカラム名を指定する.
*
* @param columnName カラム名
* @return true:スキップ、false:スキップなし
*
*/
protected boolean isSkipColumn(String columnName) {
return false;
}
/**
* 期待値とEntityデータの比較をする.
*
* @param expectedTable 期待値データ
* @param actuals 実際のデータ
* @throws Exception 期待値とEntityデータの比較中にエラーが発生した
*/
protected void assertEquals(ITable expectedTable, Object actuals)
throws Exception {
this.assertEquals(expectedTable, new Object[] { actuals });
}
/**
* 期待値とEntityデータの比較をする.
*
* @param expectedTable 期待値データ
* @param actuals 実際のデータ
* @throws Exception 期待値とEntityデータの比較中にエラーが発生した
*/
protected void assertEquals(ITable expectedTable, Object[] actuals)
throws Exception {
for (int i = 0; i < expectedTable.getRowCount(); i++) {
for (int j = 0; j < expectedTable.getTableMetaData().getColumns().length; j++) {
Column[] columns = expectedTable.getTableMetaData()
.getColumns();
String columnName = columns[j].getColumnName();
String getterName = this.buildGetterName(columnName);
Object actual = actuals[i];
Class[] paramClass = new Class[0];
Object[] paramValue = new Object[0];
Method method = actual.getClass().getDeclaredMethod(getterName,
paramClass);
assertEquals("Line:" + (i + 1) + " Column:" + columnName,
String.valueOf(expectedTable.getValue(i, columnName))
.trim(), String.valueOf(
method.invoke(actual, paramValue)).trim());
}
}
}
/**
* テストデータExcelファイルの名前を取得する.
*
* @return テストデータExcelファイル名称
*/
protected abstract String getTestFileName();
/**
* 期待値データを取得する.
*
* @param index インデックス番号
* @return 期待値データ
* @throws Exception 期待値データの取得中にエラーが発生した
*/
protected abstract ITable getExpectedTable(String index) throws Exception;
/**
* サブクラスの実行時クラスを取得する.
*
* @return 実行時クラス
*/
protected abstract Class getSubClass();
/**
* ファイル名を指定してデータセットを取得する.
*
* @param fileName ファイル名
* @return データセット
* @throws Exception データセットの取得中にエラーが発生した
*/
private IDataSet getDataSet(String fileName) throws Exception {
ReplacementDataSet expectedDataSet = new ReplacementDataSet(
new XlsDataSet(getSubClass().getResourceAsStream(fileName)));
expectedDataSet.addReplacementObject("[SYSDATE]", new Date(System
.currentTimeMillis()));
expectedDataSet.addReplacementObject("[NULL]", null);
return expectedDataSet;
}
/**
* カラム名を指定してgetterメソッド名を取得する.
*
* @param columnName カラム名
* @return getterメソッド名
*/
private String buildGetterName(String columnName) {
String getterName = "get";
String temp = columnName;
while (true) {
int index = temp.indexOf("_");
if (index == -1) {
getterName += temp.substring(0, 1).toUpperCase();
getterName += temp.substring(1).toLowerCase();
break;
}
getterName += temp.substring(0, 1).toUpperCase();
getterName += temp.substring(1, index).toLowerCase();
temp = temp.substring(index + 1);
}
return getterName;
}
/**
* データを変換する.
*
* @param data データ
* @return 変換後データ
*/
private Object convertData(Object data) {
if (data instanceof String) {
String dataStr = ((String) data).trim();
if ("true".equalsIgnoreCase(dataStr)) {
return Boolean.TRUE;
} else if ("false".equalsIgnoreCase(dataStr)) {
return Boolean.FALSE;
}
return dataStr;
} else if (data instanceof BigDecimal) {
return new Integer(((BigDecimal) data).intValue());
} else {
return data;
}
}
}
========================----sample package dbunit_sample;
import org.dbunit.dataset.ITable;
/**
* SampleDaoImplクラスのテスト.
*/
public class SampleDaoImplTest extends DaoBaseTestCase {
private static final String TABLE_NAME = TABLE_NAME_ALL[1];
private SampleDao sampleDao;
public static void main(String[] args) {
junit.textui.TestRunner.run(SampleDaoImplTest.class);
}
public SampleDaoImplTest(String arg0) {
super(arg0);
}
protected void setUp() throws Exception {
super.setUp();
this.sampleDao = new SampleDaoImpl(getConnection().getConnection());
}
protected void tearDown() throws Exception {
super.tearDown();
}
/**
* getDataメソッドの動作確認.
* データが取得されていることを確認する。
* @throws Exception
*
*/
public void testGetData() throws Exception {
assertEquals(getExpectedTable("1"), sampleDao.getData());
}
/**
* Insertメソッドの動作確認.
* データが登録されていることを確認する。
*
*/
public void testInsert() {
try {
Syohin syohin = new Syohin();
syohin.setId("5");
syohin.setName("syohin5");
syohin.setPrice(new Integer(5000));
// ロジックの実行
sampleDao.insert(syohin);
assertEquals(5, getTableRowCount(TABLE_NAME));
// 期待値とデータの比較
assertEquals(getExpectedTable("1"), getActualTable(TABLE_NAME));
} catch (Throwable e) {
fail(e.getMessage());
}
}
/**
* テストデータExcelファイルの名前を取得する.
*
* @return テストデータExcelファイル名称
*/
protected String getTestFileName() {
return "TestSampleDaoImpl_prepare.xls";
}
/**
* 期待値データを取得する.
*
* @param index インデックス番号
* @return 期待値データ
* @throws Exception 期待値データの取得中にエラーが発生した
*/
protected ITable getExpectedTable(String index) throws Exception {
return getExpectedTable(TABLE_NAME_ALL[1] + index,
"TestSampleDaoImpl_expected.xls");
}
/**
* サブクラスの実行時クラスを取得する.
*
* @return 実行時クラス
*/
protected Class getSubClass() {
return this.getClass();
}
}
事前処理の元データを退避し、テストデータを投入する機能や事後処理の元データを復元する機能、またはデータベースに登録されているデータを取得する機能などを、あらかじめ基底クラスに用意しておけば、基底クラスを継承して作成する各テストクラス作業が大変楽になります。
getConnectionは、DBコネクションを取得するメソッドです。getActualTableは、データベースに登録されているデータを取得するメソッドとなります。
getExpectedTableは、テスト結果として期待されるデータを取得するメソッドです。getTableRowCountは、データベースに登録されているデータ件数を取得するメソッドとなります。
assertTableは、期待値とデータベースに登録されているデータの比較をするメソッドです。isSkipColumnは、assertTableメソッド実行時に、比較をスキップするカラム名を指定するメソッドとなります。
assertEntityは、期待値とEntityデータの比較をするメソッドです。getTestFileNameは、テストデータExcelファイルの名前を取得するメソッドとなります。
getExpectedTableは、期待値データを取得するメソッドです。getSubClassは、サブクラスの実行時クラスを取得するメソッドとなります。
テストケース作成手順
①まず、テストクラスには基底クラスのDaoBaseTestCaseを継承させます(リスト1)。
②次に、DaoBaseTestCaseで宣言されている抽象メソッド「getTestFileName」「getTestFileName」「getSubClass」を実装します(リスト2)。
getTestFileNameは、テストデータExcelファイルの名称を指定します。getExpectedTableは、テスト結果として期待されるデータを取得します。getSubClassは、自分自身の実行時クラスを指定します。
登録(更新)系のテストで、期待値とデータベースに登録されているデータの比較をする際、期待値を予測できないカラム(例:「更新日時」など現在日時が設定されるカラム)が存在する場合は、DaoBaseTestCaseクラスのisSkipColumnメソッドをオーバーライドし、比較しない(スキップする)カラムを指定します(リスト3)。
スキップするカラムが存在しない場合、isSkipColumnメソッドはオーバーライドする必要はありません。
最後にテストアセットを作成し、テストコードを記述します。
登録(更新)系は、登録(更新)後のテーブルの値とExcelシートのデータを比較します(リスト4)。検索系は検索結果が正しいかどうかをEntityとExcelシートで比較します(リスト5)。
package dbunit_sample;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.Date;
import junit.framework.TestCase;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.Column;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.ReplacementDataSet;
import org.dbunit.dataset.excel.XlsDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.operation.DatabaseOperation;
/**
* DAOテストの基底クラス.
*/
public abstract class DaoBaseTestCase extends TestCase {
/**
* 全テーブル名.
*/
protected static final String[] TABLE_NAME_ALL;
/**
* テーブル数.
*/
private static final int TABLE_SIZE = 1;
/**
* テーブル名.
*/
private static final String TABLE_NAME = "syohin";
/**
* 元データ退避用一時ファイル.
*/
private File file;
/**
* データベースコネクション.
*/
private IDatabaseConnection connection;
/**
* テーブル名を取得する.
*/
static {
TABLE_NAME_ALL = new String[TABLE_SIZE];
for (int i = 0; i < TABLE_NAME_ALL.length; i++) {
String tableName = TABLE_NAME + (i + 1);
if (null == tableName) {
break;
}
TABLE_NAME_ALL[i] = tableName;
}
}
/**
* コンストラクタ.
*
* @param arg0 パラメータ
*/
public DaoBaseTestCase(String arg0) {
super(arg0);
}
/**
* 事前処理.
* 元データを退避し、テストデータを投入する。
*
* @throws Exception 事前処理中にエラーが発生した
*/
protected void setUp() throws Exception {
super.setUp();
try {
connection = this.getConnection();
QueryDataSet queryDataSet = new QueryDataSet(connection);
for (int i = 0; i < TABLE_NAME_ALL.length; i++) {
queryDataSet.addTable(TABLE_NAME_ALL[i]);
}
this.file = File.createTempFile("tmp", ".xml");
FlatXmlDataSet.write(queryDataSet, new FileOutputStream(this.file));
DatabaseOperation.CLEAN_INSERT.execute(connection, this
.getDataSet(getTestFileName()));
} catch (Exception e) {
if (connection != null) {
connection.close();
}
throw e;
}
}
/**
* 事後処理.
* 元データを復元する。
*
* @throws Exception 事後処理中にエラーが発生した
*/
protected void tearDown() throws Exception {
super.tearDown();
try {
DatabaseOperation.CLEAN_INSERT.execute(connection,
new FlatXmlDataSet(this.file));
} catch (Exception e) {
throw e;
} finally {
if (connection != null) {
connection.close();
}
}
}
/**
* DBコネクションを取得する.
*
* @return DBコネクション
*/
protected IDatabaseConnection getConnection() {
return ConnectionManager.getConnection();
}
/**
* データベースに登録されているデータを取得する.
*
* @param tableName テーブル名
* @return データベースに登録されているデータ
* @throws SQLException データベースに登録されているデータの
* 取得中にエラーが発生した
*/
protected ITable getActualTable(String tableName) throws Exception {
IDatabaseConnection connection = null;
try {
connection = getConnection();
return connection.createDataSet().getTable(tableName);
} catch (Exception e) {
throw e;
} finally {
if (connection != null) {
connection.close();
}
}
}
/**
* テスト結果として期待されるデータを取得する.
*
* @param tableName テーブル名
* @param fileName テストファイル名
* @return テスト結果として期待されるデータ
* @throws Exception データの取得中にエラーが発生した
*/
protected ITable getExpectedTable(String tableName, String fileName)
throws Exception {
return this.getDataSet(fileName).getTable(tableName);
}
/**
* データベースに登録されているデータ件数を取得する.
*
* @param tableName テーブル名
* @return データ件数
* @throws Exception データ件数の取得中にエラーが発生した
*/
protected int getTableRowCount(String tableName) throws Exception {
IDatabaseConnection connection = null;
try {
connection = getConnection();
return connection.createDataSet().getTable(tableName).getRowCount();
} finally {
if (connection != null) {
connection.close();
}
}
}
/**
* 期待値とデータベースに登録されているデータの比較をする.
*
* @param expectedTable 期待値データ
* @param actualTable データベースに登録されているデータ
* @throws DataSetException データの比較中にエラーが発生した
*/
protected void assertEquals(ITable expectedTable, ITable actualTable)
throws DataSetException {
for (int i = 0; i < expectedTable.getRowCount(); i++) {
for (int j = 0; j < expectedTable.getTableMetaData().getColumns().length; j++) {
Column[] columns = expectedTable.getTableMetaData()
.getColumns();
String columnName = columns[j].getColumnName();
if (this.isSkipColumn(columnName)) {
continue;
}
assertEquals("Line:" + (i + 1) + " Column:" + columnName,
convertData(expectedTable.getValue(i, columnName)),
convertData(actualTable.getValue(i, columnName)));
}
}
}
/**
* assertTableメソッド実行時、比較をスキップするカラム名を指定する.
*
* @param columnName カラム名
* @return true:スキップ、false:スキップなし
*
*/
protected boolean isSkipColumn(String columnName) {
return false;
}
/**
* 期待値とEntityデータの比較をする.
*
* @param expectedTable 期待値データ
* @param actuals 実際のデータ
* @throws Exception 期待値とEntityデータの比較中にエラーが発生した
*/
protected void assertEquals(ITable expectedTable, Object actuals)
throws Exception {
this.assertEquals(expectedTable, new Object[] { actuals });
}
/**
* 期待値とEntityデータの比較をする.
*
* @param expectedTable 期待値データ
* @param actuals 実際のデータ
* @throws Exception 期待値とEntityデータの比較中にエラーが発生した
*/
protected void assertEquals(ITable expectedTable, Object[] actuals)
throws Exception {
for (int i = 0; i < expectedTable.getRowCount(); i++) {
for (int j = 0; j < expectedTable.getTableMetaData().getColumns().length; j++) {
Column[] columns = expectedTable.getTableMetaData()
.getColumns();
String columnName = columns[j].getColumnName();
String getterName = this.buildGetterName(columnName);
Object actual = actuals[i];
Class[] paramClass = new Class[0];
Object[] paramValue = new Object[0];
Method method = actual.getClass().getDeclaredMethod(getterName,
paramClass);
assertEquals("Line:" + (i + 1) + " Column:" + columnName,
String.valueOf(expectedTable.getValue(i, columnName))
.trim(), String.valueOf(
method.invoke(actual, paramValue)).trim());
}
}
}
/**
* テストデータExcelファイルの名前を取得する.
*
* @return テストデータExcelファイル名称
*/
protected abstract String getTestFileName();
/**
* 期待値データを取得する.
*
* @param index インデックス番号
* @return 期待値データ
* @throws Exception 期待値データの取得中にエラーが発生した
*/
protected abstract ITable getExpectedTable(String index) throws Exception;
/**
* サブクラスの実行時クラスを取得する.
*
* @return 実行時クラス
*/
protected abstract Class getSubClass();
/**
* ファイル名を指定してデータセットを取得する.
*
* @param fileName ファイル名
* @return データセット
* @throws Exception データセットの取得中にエラーが発生した
*/
private IDataSet getDataSet(String fileName) throws Exception {
ReplacementDataSet expectedDataSet = new ReplacementDataSet(
new XlsDataSet(getSubClass().getResourceAsStream(fileName)));
expectedDataSet.addReplacementObject("[SYSDATE]", new Date(System
.currentTimeMillis()));
expectedDataSet.addReplacementObject("[NULL]", null);
return expectedDataSet;
}
/**
* カラム名を指定してgetterメソッド名を取得する.
*
* @param columnName カラム名
* @return getterメソッド名
*/
private String buildGetterName(String columnName) {
String getterName = "get";
String temp = columnName;
while (true) {
int index = temp.indexOf("_");
if (index == -1) {
getterName += temp.substring(0, 1).toUpperCase();
getterName += temp.substring(1).toLowerCase();
break;
}
getterName += temp.substring(0, 1).toUpperCase();
getterName += temp.substring(1, index).toLowerCase();
temp = temp.substring(index + 1);
}
return getterName;
}
/**
* データを変換する.
*
* @param data データ
* @return 変換後データ
*/
private Object convertData(Object data) {
if (data instanceof String) {
String dataStr = ((String) data).trim();
if ("true".equalsIgnoreCase(dataStr)) {
return Boolean.TRUE;
} else if ("false".equalsIgnoreCase(dataStr)) {
return Boolean.FALSE;
}
return dataStr;
} else if (data instanceof BigDecimal) {
return new Integer(((BigDecimal) data).intValue());
} else {
return data;
}
}
}
========================----sample package dbunit_sample;
import org.dbunit.dataset.ITable;
/**
* SampleDaoImplクラスのテスト.
*/
public class SampleDaoImplTest extends DaoBaseTestCase {
private static final String TABLE_NAME = TABLE_NAME_ALL[1];
private SampleDao sampleDao;
public static void main(String[] args) {
junit.textui.TestRunner.run(SampleDaoImplTest.class);
}
public SampleDaoImplTest(String arg0) {
super(arg0);
}
protected void setUp() throws Exception {
super.setUp();
this.sampleDao = new SampleDaoImpl(getConnection().getConnection());
}
protected void tearDown() throws Exception {
super.tearDown();
}
/**
* getDataメソッドの動作確認.
* データが取得されていることを確認する。
* @throws Exception
*
*/
public void testGetData() throws Exception {
assertEquals(getExpectedTable("1"), sampleDao.getData());
}
/**
* Insertメソッドの動作確認.
* データが登録されていることを確認する。
*
*/
public void testInsert() {
try {
Syohin syohin = new Syohin();
syohin.setId("5");
syohin.setName("syohin5");
syohin.setPrice(new Integer(5000));
// ロジックの実行
sampleDao.insert(syohin);
assertEquals(5, getTableRowCount(TABLE_NAME));
// 期待値とデータの比較
assertEquals(getExpectedTable("1"), getActualTable(TABLE_NAME));
} catch (Throwable e) {
fail(e.getMessage());
}
}
/**
* テストデータExcelファイルの名前を取得する.
*
* @return テストデータExcelファイル名称
*/
protected String getTestFileName() {
return "TestSampleDaoImpl_prepare.xls";
}
/**
* 期待値データを取得する.
*
* @param index インデックス番号
* @return 期待値データ
* @throws Exception 期待値データの取得中にエラーが発生した
*/
protected ITable getExpectedTable(String index) throws Exception {
return getExpectedTable(TABLE_NAME_ALL[1] + index,
"TestSampleDaoImpl_expected.xls");
}
/**
* サブクラスの実行時クラスを取得する.
*
* @return 実行時クラス
*/
protected Class getSubClass() {
return this.getClass();
}
}
JUnit DBUnit - 2
データはXML形式
テスト対象ソース AcceptOrderBean.java
import java.sql.*;
import java.util.ArrayList;
public class AcceptOrderBean {
private String o_num;
private String c_num;
private String p_num;
private int dc_rate;
private int option_price;
private String employee;
private Date accept_date;
public String getO_num() {
return o_num;
}
public String getC_num() {
return c_num;
}
public String getP_num() {
return p_num;
}
public int getDc_rate() {
return dc_rate;
}
public int getOption_price() {
return option_price;
}
public String getEmployee() {
return employee;
}
public Date getAccept_date() {
return accept_date;
}
public void setO_num(String o_num) {
this.o_num = o_num;
}
public void setC_num(String c_num) {
this.c_num = c_num;
}
public void setP_num(String p_num) {
this.p_num = p_num;
}
public void setDc_rate(int dc_rate) {
this.dc_rate = dc_rate;
}
public void setOption_price(int option_price) {
this.option_price = option_price;
}
public void setEmployee(String employee) {
this.employee = employee;
}
public void setAccept_date(Date date) {
this.accept_date = date;
}
private static Connection getConnection() throws Exception {
Class.forName(""com.mysql.jdbc.Driver"");
Connection connection = null;
// connection = DriverManager.getConnection(
// ""jdbc:mysql://localhost:3306/dbunit"", ""root"", """");
connection = DriverManager.getConnection(
""jdbc:mysql:///dbunit?useUnicode=true&charcterEncoding=UTF-8"",
""root"", """");
connection.setAutoCommit(false);
return connection;
}
public void loadByO_num(String o_num) throws Exception {
Connection connection = null;
try {
connection = getConnection();
connection.setAutoCommit(true);
String sql = ""select * from Accept_Order where o_num=?"";
PreparedStatement stat = connection.prepareStatement(sql);
stat.setString(1, o_num);
ResultSet result = stat.executeQuery();
if (result.next()) {
this.o_num = o_num;
this.c_num = result.getString(""c_num"");
this.p_num = result.getString(""p_num"");
this.dc_rate = result.getInt(""dc_rate"");
this.option_price = result.getInt(""option_price"");
this.employee = result.getString(""employee"");
this.accept_date = result.getDate(""accept_date"");
}
} finally {
if (connection != null)
connection.close();
}
}
public static AcceptOrderBean[] findByEmployee(String employee)
throws Exception {
Connection connection = null;
try {
connection = getConnection();
String sql = ""select * from Accept_Order where employee=?"";
PreparedStatement stat = connection.prepareStatement(sql);
stat.setString(1, employee);
ResultSet result = stat.executeQuery();
ArrayList list = new ArrayList();
while (result.next()) {
AcceptOrderBean accept = new AcceptOrderBean();
accept.o_num = result.getString(""o_num"");
accept.c_num = result.getString(""c_num"");
accept.p_num = result.getString(""p_num"");
accept.dc_rate = result.getInt(""dc_rate"");
accept.option_price = result.getInt(""option_price"");
accept.employee = result.getString(""employee"");
accept.accept_date = result.getDate(""accept_date"");
list.add(accept);
}
return (AcceptOrderBean[]) list.toArray(new AcceptOrderBean[list
.size()]);
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
}
}
}
public void store() throws Exception {
Connection connection = null;
try {
connection = getConnection();
String sql = ""insert into Accept_Order values(?,?,?,?,?,?,?)"";
PreparedStatement stat = connection.prepareStatement(sql);
stat.setString(1, this.o_num);
stat.setString(2, this.c_num);
stat.setString(3, this.p_num);
stat.setInt(4, this.dc_rate);
stat.setInt(5, this.option_price);
stat.setString(6, this.employee);
stat.setDate(7, this.accept_date);
stat.executeUpdate();
connection.commit();
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
}
}
}
public void delete() throws Exception {
Connection connection = null;
try {
connection = getConnection();
String sql = ""delete from Accept_Order where o_num=?"";
PreparedStatement stat = connection.prepareStatement(sql);
stat.setString(1, this.o_num);
stat.executeUpdate();
connection.commit();
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
}
}
}
}
テストケース AcceptOrderBeanTest.java
import static org.junit.Assert.*;
import org.dbunit.Assertion;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.operation.DatabaseOperation;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Date;
import java.sql.DriverManager;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class AcceptOrderBeanTest extends AcceptOrderBean {
private File file;
private static Connection getConnection() throws Exception {
Class.forName(""com.mysql.jdbc.Driver"");
Connection connection = null;
// connection = DriverManager.getConnection(
// ""jdbc:mysql://localhost:3306/dbunit"", ""root"", """");
connection = DriverManager.getConnection(
""jdbc:mysql:///dbunit?useUnicode=true&charcterEncoding=UTF-8"",
""root"", """");
// テストソースではコミット宣言はデフォルトのままでよい。
// connection.setAutoCommit(false);
return connection;
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@SuppressWarnings(""deprecation"")
@Before
public void setUp() throws Exception {
IDatabaseConnection connection = null;
try {
Connection conn = getConnection();
connection = new DatabaseConnection(conn);
// 現状のバックアップを取得
QueryDataSet partialDataSet = new QueryDataSet(connection);
partialDataSet.addTable(""ACCEPT_ORDER"");
file = File.createTempFile(""accept"", "".xml"");
FlatXmlDataSet.write(partialDataSet, new FileOutputStream(file));
// テストデータを投入する
IDataSet dataSet = new FlatXmlDataSet(new FileInputStream(
""C:/pleiades/workspace/dbtest/src/accept_order_data.xml""));
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
}
}
}
@SuppressWarnings(""deprecation"")
@After
public void tearDown() throws Exception {
IDatabaseConnection connection = null;
try {
Connection conn = getConnection();
connection = new DatabaseConnection(conn);
IDataSet dataSet = new FlatXmlDataSet(file);
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
}
}
}
@Test
public void testLoadByO_num() throws Exception {
// loadByO_numメソッドを実行
AcceptOrderBean order = new AcceptOrderBean();
order.loadByO_num(""5001"");
// 実行結果を確認
// 取得したAcceptOrderBeanの配列は「文字列」で比較する。
assertEquals(""accept_date=2004-01-01"",
""accept_date="" + order.getAccept_date());
assertEquals(""c_num=1000"", ""c_num="" + order.getC_num());
assertEquals(""dc_rate=5"", ""dc_rate="" + order.getDc_rate());
assertEquals(""employee=101"", ""employee="" + order.getEmployee());
assertEquals(""o_num=5001"", ""o_num="" + order.getO_num());
assertEquals(""option_price=30"",
""option_price="" + order.getOption_price());
assertEquals(""p_num=501"", ""p_num="" + order.getP_num());
}
@SuppressWarnings(""deprecation"")
@Test
public void testDelete() throws Exception {
// deleteメソッドを実行
AcceptOrderBean order = new AcceptOrderBean();
order.setO_num(""5003"");
order.delete();
// 実行結果を確認
IDatabaseConnection connection = null;
try {
// accept_orderテーブルの状態を確認
Connection conn = getConnection();
connection = new DatabaseConnection(conn);
IDataSet databaseDataSet = connection.createDataSet();
ITable actualTable = databaseDataSet.getTable(""accept_order"");
// あるべき姿を確認
IDataSet expectedDataSet = new FlatXmlDataSet(new FileInputStream(
""C:/pleiades/workspace/dbtest/src/accept_order_data3.xml""));
ITable expectedTable = expectedDataSet.getTable(""accept_order"");
// 比較する
Assertion.assertEquals(expectedTable, actualTable);
} finally {
if (connection != null)
connection.close();
}
}
@SuppressWarnings(""deprecation"")
@Test
public void testStore() throws Exception {
// storeメソッドの実行
AcceptOrderBean order = new AcceptOrderBean();
order.setO_num(""5004"");
order.setC_num(""1004"");
order.setP_num(""501"");
order.setDc_rate(20);
order.setOption_price(400);
order.setEmployee(""101"");
order.setAccept_date(Date.valueOf(""2004-01-03""));
order.store();
// 実行結果を検証する
IDatabaseConnection connection = null;
try {
// accept_orderテーブルの状態を確認
Connection conn = getConnection();
connection = new DatabaseConnection(conn);
IDataSet databaseDataSet = connection.createDataSet();
ITable actualTable = databaseDataSet.getTable(""accept_order"");
// 期待されるデータを取得
IDataSet expectedDataSet = new FlatXmlDataSet(new FileInputStream(
""C:/pleiades/workspace/dbtest/src/accept_order_data2.xml""));
ITable expectedTable = expectedDataSet.getTable(""accept_order"");
// 比較する
Assertion.assertEquals(expectedTable, actualTable);
} finally {
if (connection != null)
connection.close();
}
}
// @Test
public final void test() {
}
}
データ
テスト対象ソース AcceptOrderBean.java
import java.sql.*;
import java.util.ArrayList;
public class AcceptOrderBean {
private String o_num;
private String c_num;
private String p_num;
private int dc_rate;
private int option_price;
private String employee;
private Date accept_date;
public String getO_num() {
return o_num;
}
public String getC_num() {
return c_num;
}
public String getP_num() {
return p_num;
}
public int getDc_rate() {
return dc_rate;
}
public int getOption_price() {
return option_price;
}
public String getEmployee() {
return employee;
}
public Date getAccept_date() {
return accept_date;
}
public void setO_num(String o_num) {
this.o_num = o_num;
}
public void setC_num(String c_num) {
this.c_num = c_num;
}
public void setP_num(String p_num) {
this.p_num = p_num;
}
public void setDc_rate(int dc_rate) {
this.dc_rate = dc_rate;
}
public void setOption_price(int option_price) {
this.option_price = option_price;
}
public void setEmployee(String employee) {
this.employee = employee;
}
public void setAccept_date(Date date) {
this.accept_date = date;
}
private static Connection getConnection() throws Exception {
Class.forName(""com.mysql.jdbc.Driver"");
Connection connection = null;
// connection = DriverManager.getConnection(
// ""jdbc:mysql://localhost:3306/dbunit"", ""root"", """");
connection = DriverManager.getConnection(
""jdbc:mysql:///dbunit?useUnicode=true&charcterEncoding=UTF-8"",
""root"", """");
connection.setAutoCommit(false);
return connection;
}
public void loadByO_num(String o_num) throws Exception {
Connection connection = null;
try {
connection = getConnection();
connection.setAutoCommit(true);
String sql = ""select * from Accept_Order where o_num=?"";
PreparedStatement stat = connection.prepareStatement(sql);
stat.setString(1, o_num);
ResultSet result = stat.executeQuery();
if (result.next()) {
this.o_num = o_num;
this.c_num = result.getString(""c_num"");
this.p_num = result.getString(""p_num"");
this.dc_rate = result.getInt(""dc_rate"");
this.option_price = result.getInt(""option_price"");
this.employee = result.getString(""employee"");
this.accept_date = result.getDate(""accept_date"");
}
} finally {
if (connection != null)
connection.close();
}
}
public static AcceptOrderBean[] findByEmployee(String employee)
throws Exception {
Connection connection = null;
try {
connection = getConnection();
String sql = ""select * from Accept_Order where employee=?"";
PreparedStatement stat = connection.prepareStatement(sql);
stat.setString(1, employee);
ResultSet result = stat.executeQuery();
ArrayList
while (result.next()) {
AcceptOrderBean accept = new AcceptOrderBean();
accept.o_num = result.getString(""o_num"");
accept.c_num = result.getString(""c_num"");
accept.p_num = result.getString(""p_num"");
accept.dc_rate = result.getInt(""dc_rate"");
accept.option_price = result.getInt(""option_price"");
accept.employee = result.getString(""employee"");
accept.accept_date = result.getDate(""accept_date"");
list.add(accept);
}
return (AcceptOrderBean[]) list.toArray(new AcceptOrderBean[list
.size()]);
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
}
}
}
public void store() throws Exception {
Connection connection = null;
try {
connection = getConnection();
String sql = ""insert into Accept_Order values(?,?,?,?,?,?,?)"";
PreparedStatement stat = connection.prepareStatement(sql);
stat.setString(1, this.o_num);
stat.setString(2, this.c_num);
stat.setString(3, this.p_num);
stat.setInt(4, this.dc_rate);
stat.setInt(5, this.option_price);
stat.setString(6, this.employee);
stat.setDate(7, this.accept_date);
stat.executeUpdate();
connection.commit();
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
}
}
}
public void delete() throws Exception {
Connection connection = null;
try {
connection = getConnection();
String sql = ""delete from Accept_Order where o_num=?"";
PreparedStatement stat = connection.prepareStatement(sql);
stat.setString(1, this.o_num);
stat.executeUpdate();
connection.commit();
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
}
}
}
}
テストケース AcceptOrderBeanTest.java
import static org.junit.Assert.*;
import org.dbunit.Assertion;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.operation.DatabaseOperation;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Date;
import java.sql.DriverManager;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class AcceptOrderBeanTest extends AcceptOrderBean {
private File file;
private static Connection getConnection() throws Exception {
Class.forName(""com.mysql.jdbc.Driver"");
Connection connection = null;
// connection = DriverManager.getConnection(
// ""jdbc:mysql://localhost:3306/dbunit"", ""root"", """");
connection = DriverManager.getConnection(
""jdbc:mysql:///dbunit?useUnicode=true&charcterEncoding=UTF-8"",
""root"", """");
// テストソースではコミット宣言はデフォルトのままでよい。
// connection.setAutoCommit(false);
return connection;
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@SuppressWarnings(""deprecation"")
@Before
public void setUp() throws Exception {
IDatabaseConnection connection = null;
try {
Connection conn = getConnection();
connection = new DatabaseConnection(conn);
// 現状のバックアップを取得
QueryDataSet partialDataSet = new QueryDataSet(connection);
partialDataSet.addTable(""ACCEPT_ORDER"");
file = File.createTempFile(""accept"", "".xml"");
FlatXmlDataSet.write(partialDataSet, new FileOutputStream(file));
// テストデータを投入する
IDataSet dataSet = new FlatXmlDataSet(new FileInputStream(
""C:/pleiades/workspace/dbtest/src/accept_order_data.xml""));
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
}
}
}
@SuppressWarnings(""deprecation"")
@After
public void tearDown() throws Exception {
IDatabaseConnection connection = null;
try {
Connection conn = getConnection();
connection = new DatabaseConnection(conn);
IDataSet dataSet = new FlatXmlDataSet(file);
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
}
}
}
@Test
public void testLoadByO_num() throws Exception {
// loadByO_numメソッドを実行
AcceptOrderBean order = new AcceptOrderBean();
order.loadByO_num(""5001"");
// 実行結果を確認
// 取得したAcceptOrderBeanの配列は「文字列」で比較する。
assertEquals(""accept_date=2004-01-01"",
""accept_date="" + order.getAccept_date());
assertEquals(""c_num=1000"", ""c_num="" + order.getC_num());
assertEquals(""dc_rate=5"", ""dc_rate="" + order.getDc_rate());
assertEquals(""employee=101"", ""employee="" + order.getEmployee());
assertEquals(""o_num=5001"", ""o_num="" + order.getO_num());
assertEquals(""option_price=30"",
""option_price="" + order.getOption_price());
assertEquals(""p_num=501"", ""p_num="" + order.getP_num());
}
@SuppressWarnings(""deprecation"")
@Test
public void testDelete() throws Exception {
// deleteメソッドを実行
AcceptOrderBean order = new AcceptOrderBean();
order.setO_num(""5003"");
order.delete();
// 実行結果を確認
IDatabaseConnection connection = null;
try {
// accept_orderテーブルの状態を確認
Connection conn = getConnection();
connection = new DatabaseConnection(conn);
IDataSet databaseDataSet = connection.createDataSet();
ITable actualTable = databaseDataSet.getTable(""accept_order"");
// あるべき姿を確認
IDataSet expectedDataSet = new FlatXmlDataSet(new FileInputStream(
""C:/pleiades/workspace/dbtest/src/accept_order_data3.xml""));
ITable expectedTable = expectedDataSet.getTable(""accept_order"");
// 比較する
Assertion.assertEquals(expectedTable, actualTable);
} finally {
if (connection != null)
connection.close();
}
}
@SuppressWarnings(""deprecation"")
@Test
public void testStore() throws Exception {
// storeメソッドの実行
AcceptOrderBean order = new AcceptOrderBean();
order.setO_num(""5004"");
order.setC_num(""1004"");
order.setP_num(""501"");
order.setDc_rate(20);
order.setOption_price(400);
order.setEmployee(""101"");
order.setAccept_date(Date.valueOf(""2004-01-03""));
order.store();
// 実行結果を検証する
IDatabaseConnection connection = null;
try {
// accept_orderテーブルの状態を確認
Connection conn = getConnection();
connection = new DatabaseConnection(conn);
IDataSet databaseDataSet = connection.createDataSet();
ITable actualTable = databaseDataSet.getTable(""accept_order"");
// 期待されるデータを取得
IDataSet expectedDataSet = new FlatXmlDataSet(new FileInputStream(
""C:/pleiades/workspace/dbtest/src/accept_order_data2.xml""));
ITable expectedTable = expectedDataSet.getTable(""accept_order"");
// 比較する
Assertion.assertEquals(expectedTable, actualTable);
} finally {
if (connection != null)
connection.close();
}
}
// @Test
public final void test() {
}
}
データ
<?xml version='1.0' encoding="UTF-8"?>
<dataset>
<ACCEPT_ORDER o_num="5001" c_num="1000" p_num="501" dc_rate="5"
option_price="30" employee="101" accept_date="2004-01-01" />
<ACCEPT_ORDER o_num="5002" c_num="1001" p_num="502" dc_rate="10"
option_price="20" employee="104"
accept_date="2004-02-02" />
<ACCEPT_ORDER o_num="5003" c_num="1002" p_num="503" dc_rate="15"
option_price="10" employee="101" accept_date="2004-01-31" />
</dataset>
<?xml version='1.0' encoding="UTF-8"?>
<dataset>
<ACCEPT_ORDER o_num="5001" c_num="1000" p_num="501" dc_rate="5"
option_price="30" employee="101" accept_date="2004-01-01" />
<ACCEPT_ORDER o_num="5002" c_num="1001" p_num="502" dc_rate="10"
option_price="20" employee="104"
accept_date="2004-02-02" />
<ACCEPT_ORDER o_num="5003" c_num="1002" p_num="503" dc_rate="15"
option_price="10" employee="101" accept_date="2004-01-31" />
<ACCEPT_ORDER o_num="5004" c_num="1004" p_num="501" dc_rate="20"
option_price="400" employee="101" accept_date="2004-01-03" />
</dataset>
<?xml version='1.0' encoding="UTF-8"?> <dataset> <ACCEPT_ORDER o_num="5001" c_num="1000" p_num="501" dc_rate="5" option_price="30" employee="101" accept_date="2004-01-01" /> <ACCEPT_ORDER o_num="5002" c_num="1001" p_num="502" dc_rate="10" option_price="20" employee="104" accept_date="2004-02-02" /> </dataset>
JUnit DBUnit - 1
◆必要なライブラリ
dbunit-2.4.9.jar
junit-4.10.jar
log4j-1.2.15.jar
slf4j-api-1.7.5.jar
slf4j-ext-1.7.5.jar
slf4j-log4j12-1.7.5.jar
slf4j-migrator-1.7.5.jar
◆ロギングがないとエラーが発生します。
「java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory」
◆LOG4Jのダウンロード先
http://logging.apache.org/log4j/1.2/
dbunit-2.4.9.jar
junit-4.10.jar
log4j-1.2.15.jar
slf4j-api-1.7.5.jar
slf4j-ext-1.7.5.jar
slf4j-log4j12-1.7.5.jar
slf4j-migrator-1.7.5.jar
◆ロギングがないとエラーが発生します。
「java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory」
◆LOG4Jのダウンロード先
http://logging.apache.org/log4j/1.2/
2013年5月24日金曜日
ファイルシステムとディレクトリ
■ファイルシステムの基礎
通常OSを操作する上において、データを操作するということはファイルを操作することとほぼ同等の意味と解釈されます。
このため通常はデータが物理的にどのように格納されているかを意識する必要もなく、逆に意識できない作りになっています。
これはOSの機能であるデータの抽象化によってもたらされる結果です。
実際に物理的な記憶装置に格納されているデータを素で扱おうとすると、HDDのどこのセクタのどの部分とどの部分を取り出して、データをメモリ上に読み込み、メモリに格納できないものは後からまた読む・・・といった大変面倒なことをする必要があります。
現在のHDDの容量を考えてみると、テラ単位・・・という巨大なデータを格納できるような物体です。その中に入っている何かのデータを自力で探しだし、利用することは人間が行う作業として成立しないものだともいえます。このような物体に存在するデータを人間にわかりやすいファイルというものに抽象化、可視化し、データを永続的に管理しやすくするという役目を持つのがファイルシステムの基本的な考え方です。
ファイルシステムにはほかにもキャッシュメモリを利用して効果的にHDDなどの二次記憶装置を使うという機能も含まれており、Linuxではこの機能によって大きなファイルでも高速にアクセスすることが可能になっています。このようにファイルシステムは単にデータを扱いやすく管理するだけではなくH/Wリソースを効率よく利用するための機能も含まれています。
■VFS(Virtual File System)
Linuxでは全てをファイルとして扱うようになっています。これは通常のデータファイルだけではなく、HDD、CD-ROM、マウスなど様々なデバイスもファイルとして扱う仕組みになっています。
この仕組みを提供するのがVFSという仮想的なファイルシステムです。VFSは下位の物理的な媒体を抽象化し、データ、デバイスを含む全てのコンピュータリソースに対して統一的なファイルアクセスという入出力インターフェイスを提供します。これによりリソースの差異を気にすることなく、様々な対象に対して統一のアクセスを行うことができます。
さらにVFSにはファイルシステムの抽象化という役割もあり、これによってことなる複数のファイルシステムに対して透過的にアクセスすることが可能になっており、ファイルシステムの種類を意識せずに利用することができます。VFSは、プログラムからのファイルシステムの操作を固有のファイルシステムの操作に変換します。このためユーザが実行するプログラムはファイルシステムの差異を意識する必要がなく、統一的なアクセス方法で実際にはことなるファイルシステムにアクセスできます。
■マウント
通常LinuxではHDDを複数のパーティションに分割して、それぞれにファイルシステムを作成します。そして各ファイルシステムをディレクトリツリーの/(ルート)以下にマウントすることで利用できるようにしますが、このマウントという考え方は重要でファイルシステムを利用するときには必ず行わなければならない操作でもあります。
Linuxのファイルシステムはツリー構造になっていますがこの構造を木の根にたとえると、その木の根に接ぎ木をする行為がマウントになります。つまりOSから対象となるH/WをS/W的に接続する行為がマウントになります。
例えばHDDを物理的に追加したことを想定してみます。
基本的にLinuxではストレージのH/Wが認識されても、それだけではファイルシステムとして利用することができません。つまりファイルを保存したりするなどの行為ができないのです。Linux上ではH/Wの認識とファイルシステムとして利用可能であるかは別の問題であり、ファイルシステムとして利用可能にするためには任意であれ、自動であれマウントすることが必要不可欠です。
※最近ではデスクトップ環境などに自動マウント機能を活用する仕組みが導入されているので、USBメモリなどのストレージも挿せば認識、マウントが自動的に行われるためあまり意識はしなくなっているかもしれません。
このため手順としては以下のようなフローでHDDを追加することになります。
・HDDの物理フォーマット、パーティションを切るなどの作業
・各パーティションにファイルシステムを構築し、フォーマットする
・マウントポイントを指定し、ディレクトリツリーのどこかにマウントを行う
マウントできるデバイスターゲットはブロックデバイスといわれる、HDD、CD-ROMなどのストレージと呼ばれるディスクドライブです。またマウントしたデバイスの物理的接続解除を行うためには、アンマウントというS/W的接続解除の手続きを行って、物理的接続解除を行う必要があります。アンマウントを行わずに強引に物理的に接続解除をするとデータを破損する危険があります。
このようにLinuxにはWindowsなどとは異なるファイルシステムの概念があり、このシステムによってデータを効率よく管理することができるようになっています。OSの基本操作であるファイル操作は頻度の高い操作系ですが、OSごとのファイル管理の概念に沿って操作が行われます。このため通常は意識しないような仕組みの部分を知ることによって、ファイル操作の考え方をより深く理解することができます。
備考:ディレクトリ
ファイル以外のデータ管理の単位としてディレクトリがあります。ディレクトリはファイルをまとめる存在ですが、物理的なデータとしてディレクトリが存在しているわけではありません。ディレクトリはOSがファイルをまとめるために利用する論理的な存在で、様々な情報から成り立つファイル情報の集合体だといえます。このためファイル操作にはディレクトリ内にある情報が不可欠で、lsなどのファイル一覧表示機能もディレクトリの情報を利用しています。Linuxのディレクトリツリーの頂点が/(ルート)であることからも、ファイルは全て各階層のディレクトリによって管理されていることがわかります。
通常OSを操作する上において、データを操作するということはファイルを操作することとほぼ同等の意味と解釈されます。
このため通常はデータが物理的にどのように格納されているかを意識する必要もなく、逆に意識できない作りになっています。
これはOSの機能であるデータの抽象化によってもたらされる結果です。
実際に物理的な記憶装置に格納されているデータを素で扱おうとすると、HDDのどこのセクタのどの部分とどの部分を取り出して、データをメモリ上に読み込み、メモリに格納できないものは後からまた読む・・・といった大変面倒なことをする必要があります。
現在のHDDの容量を考えてみると、テラ単位・・・という巨大なデータを格納できるような物体です。その中に入っている何かのデータを自力で探しだし、利用することは人間が行う作業として成立しないものだともいえます。このような物体に存在するデータを人間にわかりやすいファイルというものに抽象化、可視化し、データを永続的に管理しやすくするという役目を持つのがファイルシステムの基本的な考え方です。
ファイルシステムにはほかにもキャッシュメモリを利用して効果的にHDDなどの二次記憶装置を使うという機能も含まれており、Linuxではこの機能によって大きなファイルでも高速にアクセスすることが可能になっています。このようにファイルシステムは単にデータを扱いやすく管理するだけではなくH/Wリソースを効率よく利用するための機能も含まれています。
■VFS(Virtual File System)
Linuxでは全てをファイルとして扱うようになっています。これは通常のデータファイルだけではなく、HDD、CD-ROM、マウスなど様々なデバイスもファイルとして扱う仕組みになっています。
この仕組みを提供するのがVFSという仮想的なファイルシステムです。VFSは下位の物理的な媒体を抽象化し、データ、デバイスを含む全てのコンピュータリソースに対して統一的なファイルアクセスという入出力インターフェイスを提供します。これによりリソースの差異を気にすることなく、様々な対象に対して統一のアクセスを行うことができます。
さらにVFSにはファイルシステムの抽象化という役割もあり、これによってことなる複数のファイルシステムに対して透過的にアクセスすることが可能になっており、ファイルシステムの種類を意識せずに利用することができます。VFSは、プログラムからのファイルシステムの操作を固有のファイルシステムの操作に変換します。このためユーザが実行するプログラムはファイルシステムの差異を意識する必要がなく、統一的なアクセス方法で実際にはことなるファイルシステムにアクセスできます。
■マウント
通常LinuxではHDDを複数のパーティションに分割して、それぞれにファイルシステムを作成します。そして各ファイルシステムをディレクトリツリーの/(ルート)以下にマウントすることで利用できるようにしますが、このマウントという考え方は重要でファイルシステムを利用するときには必ず行わなければならない操作でもあります。
Linuxのファイルシステムはツリー構造になっていますがこの構造を木の根にたとえると、その木の根に接ぎ木をする行為がマウントになります。つまりOSから対象となるH/WをS/W的に接続する行為がマウントになります。
例えばHDDを物理的に追加したことを想定してみます。
基本的にLinuxではストレージのH/Wが認識されても、それだけではファイルシステムとして利用することができません。つまりファイルを保存したりするなどの行為ができないのです。Linux上ではH/Wの認識とファイルシステムとして利用可能であるかは別の問題であり、ファイルシステムとして利用可能にするためには任意であれ、自動であれマウントすることが必要不可欠です。
※最近ではデスクトップ環境などに自動マウント機能を活用する仕組みが導入されているので、USBメモリなどのストレージも挿せば認識、マウントが自動的に行われるためあまり意識はしなくなっているかもしれません。
このため手順としては以下のようなフローでHDDを追加することになります。
・HDDの物理フォーマット、パーティションを切るなどの作業
・各パーティションにファイルシステムを構築し、フォーマットする
・マウントポイントを指定し、ディレクトリツリーのどこかにマウントを行う
マウントできるデバイスターゲットはブロックデバイスといわれる、HDD、CD-ROMなどのストレージと呼ばれるディスクドライブです。またマウントしたデバイスの物理的接続解除を行うためには、アンマウントというS/W的接続解除の手続きを行って、物理的接続解除を行う必要があります。アンマウントを行わずに強引に物理的に接続解除をするとデータを破損する危険があります。
このようにLinuxにはWindowsなどとは異なるファイルシステムの概念があり、このシステムによってデータを効率よく管理することができるようになっています。OSの基本操作であるファイル操作は頻度の高い操作系ですが、OSごとのファイル管理の概念に沿って操作が行われます。このため通常は意識しないような仕組みの部分を知ることによって、ファイル操作の考え方をより深く理解することができます。
備考:ディレクトリ
ファイル以外のデータ管理の単位としてディレクトリがあります。ディレクトリはファイルをまとめる存在ですが、物理的なデータとしてディレクトリが存在しているわけではありません。ディレクトリはOSがファイルをまとめるために利用する論理的な存在で、様々な情報から成り立つファイル情報の集合体だといえます。このためファイル操作にはディレクトリ内にある情報が不可欠で、lsなどのファイル一覧表示機能もディレクトリの情報を利用しています。Linuxのディレクトリツリーの頂点が/(ルート)であることからも、ファイルは全て各階層のディレクトリによって管理されていることがわかります。
2013年5月23日木曜日
WebSphere Application Server for Developers V8.0 導入手順
参考となるURL
http://www.ibm.com/developerworks/jp/websphere/library/was/was8_dev/
【手順の概要】
オフライン環境のための導入は、次の手順で行います。
準備の作業はオンライン環境で行ないます。
•準備
1.インターネットに接続できる環境でインストレーション・マネジャー、パッケージング・ユーティリティをダウンロードします。
2.パッケージング・ユーティリティを導入し、WAS V8.0開発者版導入リポジトリーをダウンロードします。
3.リポジトリーとインストレーション・マネジャーをDVDなどにコピーします。
•導入
1.インストレーション・マネジャーを導入します。
2.インストレーション・マネジャーで導入元のリポジトリーを設定します。
3.WAS V8.0開発者番を導入します。
オンラインであれば、インストレーション・マネジャーをダウンロードし、セットアップすることによって、Websphereを導入することができる。
下記は いずれもIBMから提供されている無料の開発用、Websphereである。
WebSphere Application Server for Developers V8.5
WebSphere Application Server for Developers V8.0
インストレーション・マネジャーで、1.5GB近くをダウンロードすることになるので、ネット環境は重要である。
IBM WebSphere Application Server Developer Tools for Eclipseは、
Eclipse Marketplace (「ヘルプ」 > 「Eclipse Marketplace」) からインストールできます。
このタスクについて
Eclipse Marketplace からは、最新バージョンの IBM WebSphere Application Server Developer Tools for Eclipse のみが入手できます。
最新バージョンの IBM WebSphere Application Server Developer Tools for Eclipse のインストール手順については、
下記のURLを参照
http://publib.boulder.ibm.com/infocenter/radhelp/v8r5/index.jsp?topic=%2Fcom.ibm.rad.install.doc%2Ftopics%2Ft_install_wdt_eclipse.html
http://www.ibm.com/developerworks/jp/websphere/library/was/was8_dev/
【手順の概要】
オフライン環境のための導入は、次の手順で行います。
準備の作業はオンライン環境で行ないます。
•準備
1.インターネットに接続できる環境でインストレーション・マネジャー、パッケージング・ユーティリティをダウンロードします。
2.パッケージング・ユーティリティを導入し、WAS V8.0開発者版導入リポジトリーをダウンロードします。
3.リポジトリーとインストレーション・マネジャーをDVDなどにコピーします。
•導入
1.インストレーション・マネジャーを導入します。
2.インストレーション・マネジャーで導入元のリポジトリーを設定します。
3.WAS V8.0開発者番を導入します。
オンラインであれば、インストレーション・マネジャーをダウンロードし、セットアップすることによって、Websphereを導入することができる。
下記は いずれもIBMから提供されている無料の開発用、Websphereである。
WebSphere Application Server for Developers V8.5
WebSphere Application Server for Developers V8.0
インストレーション・マネジャーで、1.5GB近くをダウンロードすることになるので、ネット環境は重要である。
IBM WebSphere Application Server Developer Tools for Eclipseは、
Eclipse Marketplace (「ヘルプ」 > 「Eclipse Marketplace」) からインストールできます。
このタスクについて
Eclipse Marketplace からは、最新バージョンの IBM WebSphere Application Server Developer Tools for Eclipse のみが入手できます。
最新バージョンの IBM WebSphere Application Server Developer Tools for Eclipse のインストール手順については、
下記のURLを参照
http://publib.boulder.ibm.com/infocenter/radhelp/v8r5/index.jsp?topic=%2Fcom.ibm.rad.install.doc%2Ftopics%2Ft_install_wdt_eclipse.html
2013年5月18日土曜日
備考
http://dream.mods.jp/servlet500gokui/
http://aoyama-systems.blogspot.jp/
ttp://java.keicode.com/lang/variables.php
http://www.javadrive.jp/
http://www.javaroad.jp/index.htm
http://www.techscore.com/
http://www-06.ibm.com/software/jp/websphere/apptransaction/
http://www.techscore.com/tech/Java/ApacheJakarta/Maven/4/
http://www.java-kuche.org/ryukyu-u/framework.html#i17
http://si.comp.ae.keio.ac.jp/web_app_dev_material/list.html
http://aoyamasys.jimdo.com
http://aoyama-systems.blogspot.jp/
ttp://java.keicode.com/lang/variables.php
http://www.javadrive.jp/
http://www.javaroad.jp/index.htm
http://www.techscore.com/
http://www-06.ibm.com/software/jp/websphere/apptransaction/
http://www.techscore.com/tech/Java/ApacheJakarta/Maven/4/
http://www.java-kuche.org/ryukyu-u/framework.html#i17
http://si.comp.ae.keio.ac.jp/web_app_dev_material/list.html
http://aoyamasys.jimdo.com
2013年5月12日日曜日
websphere 起動方法
ミドルウェアの起動順序・停止順序は、ミドルウェア間やプロセス間の依存関係に基づいて決定します。連携している一連のミ
ドルウェアの起動作業は、バックエンド・システムから実施することが一般的です。ミドルウェアの停止作業はその逆となり、
フロントエンド・システムから実施することが一般的です。
例えば、WASからDB2やWebSphere MQを参照している場合、起動順序はDB2やWebSphere MQを起動した上でIHSとWASを起動
する流れにし、停止順序はIHSとWASを停止した上でDB2やWebSphere MQを停止する流れにします。
上記ではIHSとWASを一括りにしましたが、IHS、WASは以下のような複数のプロセスで構成されており、これらのプロセスの起
動順序・停止順序についても考慮が必要になります。
• IHSプロセス
• (IHS管理サーバー・プロセス)
• アプリケーション・サーバー・プロセス
• デプロイメント・マネージャー・プロセス
• ノード・エージェント・プロセス IHS、WASを構成するプロセスの起動は、管理する側から管理される側という順序で実施することが一般的です。停止はその逆
となり、管理される側から管理する側という順序で実施することが一般的です。
IHS、WASの起動 IHSプロセス:
起動コマンド
UNIX/Linux環境:/bin/apachectl start
Windows環境:\bin\httpd.exe -k start -n
停止コマンド
UNIX/Linux環境:/bin/apachectl stop
Windows環境:\bin\httpd.exe -k stop -n
IHS管理サーバーは、IHSをデプロイメント・マネージャーの管理対象として統合する構成において、IHSをノード・エージェント経由で管理しない場合に必要になります。主にIHSとWASを共存させない構成で使用します。
IHS管理サーバー・プロセスの起動・停止には、
UNIX/Linux環境ではadminctlコマンド、
Windows環境ではhttpd.exeコマンド(Windowsサービス)
を使用します。
IHS管理サーバー・プロセス:
起動コマンド
UNIX/Linux環境:/bin/adminctl start
Windows環境:\bin\httpd.exe -k start -n
停止コマンド
UNIX/Linux環境:/bin/adminctl stop
Windows環境:\bin\httpd.exe -k stop -n
WASプロセスの起動・停止
WASの主なプロセスには、デプロイメント・マネージャー・プロセス、ノード・エージェント・プロセス、アプリケーション・サーバー・プロセスがあります。各プロセスの起動・停止にはWASコマンド行ツールを使用します。なお、管理セキュリティー機能を有効化している環境では、各プロセスを停止する際に認証のための管理ユーザー名とパスワードが必要になります。
デプロイメント・マネージャー・プロセスとノード・エージェント・プロセスが起動している状態であれば、WAS管理コンソールからアプリケーション・サーバー・プロセスの起動・停止を行うことも可能です。
デプロイメント・マネージャー・プロセス:
起動コマンド
UNIX/Linux環境:/bin/startManager.sh
Windows環境:\bin\startManager.bat
停止コマンド
UNIX/Linux環境:/bin/stopManager.sh [ -user <管理ユーザー名> -password <パスワード> ]
Windows環境:\bin\stopManager.bat [ -user <管理ユーザー名> -password <パスワード> ]
ノード・エージェント・プロセス:
起動コマンド
UNIX/Linux環境:/bin/startNode.sh
Windows環境:\bin\startNode.bat
停止コマンド
UNIX/Linux環境:/bin/stopNode.sh [ -user <管理ユーザー名> -password <パスワード> ]
Windows環境:\bin\stopNode.bat [ -user <管理ユーザー名> -password <パスワード> ]
アプリケーション・サーバー・プロセス:
起動コマンド
UNIX/Linux環境:/bin/startServer.sh <サーバー名>
Windows環境:\bin\startServer.bat <サーバー名>
停止コマンド
UNIX/Linux環境:/bin/stopServer.sh <サーバー名> [ -user <管理ユーザー名> -password <パスワード> ]
Windows環境:\bin\stopServer.bat <サーバー名> [ -user <管理ユーザー名> -password <パスワード> ]
Windows環境では、Windowsサービスを使用してデプロイメント・マネージャー・プロセスの起動・停止、ノード・エージェント・プロセスの起動・停止を行うように設定することも可能です。Windowsサービスへの登録にはWASService.exeコマンドを使用します。
ドルウェアの起動作業は、バックエンド・システムから実施することが一般的です。ミドルウェアの停止作業はその逆となり、
フロントエンド・システムから実施することが一般的です。
例えば、WASからDB2やWebSphere MQを参照している場合、起動順序はDB2やWebSphere MQを起動した上でIHSとWASを起動
する流れにし、停止順序はIHSとWASを停止した上でDB2やWebSphere MQを停止する流れにします。
上記ではIHSとWASを一括りにしましたが、IHS、WASは以下のような複数のプロセスで構成されており、これらのプロセスの起
動順序・停止順序についても考慮が必要になります。
• IHSプロセス
• (IHS管理サーバー・プロセス)
• アプリケーション・サーバー・プロセス
• デプロイメント・マネージャー・プロセス
• ノード・エージェント・プロセス IHS、WASを構成するプロセスの起動は、管理する側から管理される側という順序で実施することが一般的です。停止はその逆
となり、管理される側から管理する側という順序で実施することが一般的です。
IHS、WASの起動 IHSプロセス:
起動コマンド
UNIX/Linux環境:
Windows環境:
停止コマンド
UNIX/Linux環境:
Windows環境:
IHS管理サーバーは、IHSをデプロイメント・マネージャーの管理対象として統合する構成において、IHSをノード・エージェント経由で管理しない場合に必要になります。主にIHSとWASを共存させない構成で使用します。
IHS管理サーバー・プロセスの起動・停止には、
UNIX/Linux環境ではadminctlコマンド、
Windows環境ではhttpd.exeコマンド(Windowsサービス)
を使用します。
IHS管理サーバー・プロセス:
起動コマンド
UNIX/Linux環境:
Windows環境:
停止コマンド
UNIX/Linux環境:
Windows環境:
WASプロセスの起動・停止
WASの主なプロセスには、デプロイメント・マネージャー・プロセス、ノード・エージェント・プロセス、アプリケーション・サーバー・プロセスがあります。各プロセスの起動・停止にはWASコマンド行ツールを使用します。なお、管理セキュリティー機能を有効化している環境では、各プロセスを停止する際に認証のための管理ユーザー名とパスワードが必要になります。
デプロイメント・マネージャー・プロセスとノード・エージェント・プロセスが起動している状態であれば、WAS管理コンソールからアプリケーション・サーバー・プロセスの起動・停止を行うことも可能です。
デプロイメント・マネージャー・プロセス:
起動コマンド
UNIX/Linux環境:
Windows環境:
停止コマンド
UNIX/Linux環境:
Windows環境:
ノード・エージェント・プロセス:
起動コマンド
UNIX/Linux環境:
Windows環境:
停止コマンド
UNIX/Linux環境:
Windows環境:
アプリケーション・サーバー・プロセス:
起動コマンド
UNIX/Linux環境:
Windows環境:
停止コマンド
UNIX/Linux環境:
Windows環境:
Windows環境では、Windowsサービスを使用してデプロイメント・マネージャー・プロセスの起動・停止、ノード・エージェント・プロセスの起動・停止を行うように設定することも可能です。Windowsサービスへの登録にはWASService.exeコマンドを使用します。
登録:
投稿 (Atom)