基于spring testcontext+junit+dbunit的單元測試
/**
*
*/
package com.um.vstore.portal.service.impl;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import javax.sql.DataSource;
import net.sf.json.JSONObject;
import org.dbunit.Assertion;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.AmbiguousTableNameException;
import org.dbunit.database.DatabaseConnection;
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.ITableIterator;
import org.dbunit.dataset.ITableMetaData;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.dataset.xml.FlatXmlProducer;
import org.dbunit.operation.DatabaseOperation;
import org.dbunit.util.fileloader.DataFileLoader;
import org.dbunit.util.fileloader.FlatXmlDataFileLoader;
import org.dbunit.util.fileloader.XlsDataFileLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.xml.sax.InputSource;
import com.um.vstore.portal.domain.VPayTypeNote;
import com.um.vstorel.fw.common.util.DateUtil;
/**
* 以VPayTypeNote為測試示例
* <p>
* 這裡示範資料儲存、查詢的單元測試示例<br>
*
* <li>@RunWith(SpringJUnit4ClassRunner.class):注解一個junit的運作器,該運作器是用來結合spring
* test和junit的 , 它把spring test無縫運作在junit中
* <li>@ContextConfiguration(locations = { "classpath*:/applicationContext.xml"
* }):配置加載spring的配置檔案
* <li>@TransactionConfiguration(defaultRollback =
* true):配置事物處理,是否復原。當然,事物的復原也可在方法前注釋配置@Rollback(false/true)
*
* @author sg
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:/applicationContext.xml" })
// @TransactionConfiguration(defaultRollback = true)
public class VPayNotesServiceImplTest {
/**
* Test method for
* {@link com.um.vstore.portal.service.impl.VPayNotesServiceImpl#insert(com.um.vstore.portal.domain.VPayTypeNote)}
* .
*/
@Autowired
private VPayNotesServiceImpl payNotesService;
@Autowired
private DataSource dataSource;
private IDatabaseConnection conn = null;
private File file = null;// 資料表備份檔案
private static String BACK_FILE_NAME = "paynote_back";// 備份檔案名
private static String BACK_FILE_PREFIX = ".xml";// 備份檔案字尾
/**
* 測試前初始化,擷取dbunit資料庫連接配接,并對表做資料備份
*/
@Before
public void init() {
conn = getDatabaseConnection("VSTORE");
// 測試前做資料備份
QueryDataSet queryDataSet = getQueryDataSet(conn, "V_P_PAYNOTE", null);
try {
file = File.createTempFile(BACK_FILE_NAME, BACK_FILE_PREFIX);
} catch (IOException e1) {
e1.printStackTrace();
}// 備份檔案
try {
FlatXmlDataSet.write(queryDataSet, new FileOutputStream(file));
} catch (DataSetException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
closeConnection();
}
}
/**
* 測試完了對資料表原始資料進行還原
*/
@After
public void after() {
InputSource xmlSource = null;
try {
xmlSource = new InputSource(new FileInputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 解析一個檔案,産生一個資料集
FlatXmlProducer flatXmlProducer = new FlatXmlProducer(xmlSource);
flatXmlProducer.setColumnSensing(true);
try {
conn = getDatabaseConnection("VSTORE");
// 清楚資料庫資料并插入備份資料
DatabaseOperation.CLEAN_INSERT.execute(conn, new FlatXmlDataSet(
flatXmlProducer));
} catch (DataSetException e) {
e.printStackTrace();
} catch (DatabaseUnitException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeConnection();
}
}
/**
* 測試插入操作
* <p>
* 這個地方是簡單的插入測試,沒有使用dbunit,隻是簡單判斷插入的動作有沒有問題,并沒有檢查插入的資料的正确性,這種測試是不嚴謹的
* <p>
* 這個地方可以用@Transactional來注解是否使用事物,如果用事物,則配置,不用事物則不配置
*/
@Test
// @Transactional
public void testInsert() {
VPayTypeNote payTypeNote = new VPayTypeNote();
payTypeNote.setName("測試");
payTypeNote.setNotes("測試umpay..........");
payTypeNote.setPortalNo(getId());
payTypeNote.setPayType((short) 0);
payTypeNote.setStatus((short) 1);
try {
payNotesService.insert(payTypeNote);
} catch (Exception e) {
assertTrue("插入note資料異常...", false);
}
assertTrue("插入資料成功", true);
}
/**
* 測試插入操作
* <p>
* 這個測試操作我們采用了dbunit,測試操作的資料,我們應該在其儲存或修改後,取出該條資料和我們預期的資料做對比,驗證資料的正确性,當然,
* 你也可以做儲存失敗的測試,用@ExpectedException(xxxException.class)來做
*/
@Test
public void testDbInsert() {
// 手動設定測試資料
String no = getId();
VPayTypeNote payTypeNote = new VPayTypeNote();
payTypeNote.setNotes("db測試");
payTypeNote.setPortalNo(no);
payTypeNote.setPayType((short) 0);
payTypeNote.setStatus((short) 1);
try {
payNotesService.insert(payTypeNote);
conn = getDatabaseConnection("VSTORE");
// 查詢插入的資料到資料集,準備檢查是否是我們插入的資料
QueryDataSet queryDataSet = getQueryDataSet(
conn,
"V_P_PAYNOTE",
"select PAYTYPE,NOTES,STATUS from VSTORE.V_P_PAYNOTE where NOTES = 'db測試' and PAYTYPE = 0 and STATUS = 1");
if (queryDataSet == null) {
assertTrue("手動設定擷取queryDataSet為空", false);
return;
}
IDataSet expected = loadXMLDataSet("/com/um/vstore/portal/service/impl/paynote_xml.xml");
if (expected == null) {
assertTrue("手動設定側四加載預期資料失敗", false);
return;
}
Assertion.assertEquals(expected, queryDataSet);
} catch (Exception e) {
e.printStackTrace();
} finally {
closeConnection();
}
}
/**
* 測試插入操作
* <p>
* 這個測試操作我們采用了dbunit,測試操作的資料,我們應該在其儲存或修改後,取出該條資料和我們預期的資料做對比,驗證資料的正确性,當然,
* 你也可以做儲存失敗的測試,用@ExpectedException(xxxException.class)來做
*/
@Test
public void testDbInsertFromLocal() {
VPayTypeNote payTypeNote = null;
// 從外部加載自定義xml或者excel資料
Map<String, Map<Integer, String>> map = loadLocalData("/com/um/vstore/portal/service/impl/note.xml");
if (map != null && map.size() != 0) {
Set<String> set = map.keySet();
for (String key : set) {// 如果這個地方你知道自己定義得表資料隻有一個,你可以直接擷取該表資料,不用周遊所有得表
Map<Integer, String> rowMap = map.get(key);
if (rowMap != null && rowMap.size() != 0) {
Set<Integer> rowSet = rowMap.keySet();
for (Integer rowKey : rowSet) {
String row = rowMap.get(rowKey);
JSONObject jsonObj = JSONObject.fromObject(row);
payTypeNote = new VPayTypeNote();
payTypeNote.setNotes(jsonObj.getString("NOTES"));
payTypeNote.setPortalNo(getId());
payTypeNote.setPayType((short) jsonObj
.getInt("PAYTYPE"));
payTypeNote.setStatus((short) jsonObj.getInt("STATUS"));
try {
payNotesService.insert(payTypeNote);
conn = getDatabaseConnection("VSTORE");
// 查出我們剛才插入的資料,看是否是我們剛才插入的資料
QueryDataSet queryDataSet = getQueryDataSet(
conn,
"V_P_PAYNOTE",
"select PAYTYPE,NOTES,STATUS from VSTORE.V_P_PAYNOTE where NOTES = 'db測試--xml' and PAYTYPE = 0 and STATUS = 1");
if (queryDataSet == null) {
assertTrue("自定義設定擷取queryDataSet為空", false);
return;
}
IDataSet expected = loadXMLDataSet("/com/um/vstore/portal/service/impl/note.xml");
if (expected == null) {
assertTrue("自定義設定側四加載預期資料失敗", false);
return;
}
// 這個地方用的是dbunit的斷言,比較兩個資料集
Assertion.assertEquals(expected, queryDataSet);
} catch (Exception e) {
e.printStackTrace();
} finally {
closeConnection();
}
}
} else {
assertTrue("表格[" + key + "]擷取行資料異常", false);
}
}
assertTrue("擷取行xml資料正常", true);
} else {
assertTrue("xml測試資料沒有擷取到值", false);
}
}
/**
* 測試資料查詢
* <p>
* 我們定義一個預期的資料,該資料通過dbunit初始化到資料庫中,然後我們通過查詢檢索出這些資料,和預期資料做個對比,看我們的檢索是否正确
*/
@Test
public void testSearchForPrimaryKey() {
// 加載自定義的預期資料
IDataSet ds = loadXMLDataSet("/com/um/vstore/portal/service/impl/search_note.xml");
try {
conn = getDatabaseConnection("VSTORE");
// 把準備的預期資料通過dbunit持久化到資料庫表中
DatabaseOperation.CLEAN_INSERT.execute(conn, ds);
} catch (DatabaseUnitException e1) {
e1.printStackTrace();
} catch (SQLException e1) {
e1.printStackTrace();
}
try {
VPayTypeNote note = payNotesService.searchForPrimaryKey("10000001",
(short) 0, (short) 1);
if (note == null) {
assertTrue("查詢測試失敗", false);
} else {
assertTrue(true);
}
} catch (Exception e) {
e.printStackTrace();
assertTrue(false);
} finally {
closeConnection();
}
}
/**
* 測試擷取excel中的資料
* <p>
* 擷取excel中的測試資料,封裝該資料可以用來測試外部提供資料進行批量插入,及查詢等的預期資料
* <p>
* 這個dbunit需要依賴poi3.2的包
*/
@Test
public void testExcelLoad() {
Map<String, Map<Integer, String>> map = loadLocalData("/com/um/vstore/portal/service/impl/note.xls");
if (map != null && map.size() != 0) {
Set<String> set = map.keySet();
for (String key : set) {
Map<Integer, String> rowMap = map.get(key);
if (rowMap != null && rowMap.size() != 0) {
Set<Integer> rowSet = rowMap.keySet();
for (Integer rowKey : rowSet) {
String row = rowMap.get(rowKey);
System.out.println(row);
}
} else {
assertTrue("表格[" + key + "]擷取行資料異常", false);
}
}
assertTrue("擷取行excel資料正常", true);
} else {
assertTrue("excel測試資料沒有擷取到值", false);
}
}
@Test
/**
* 測試擷取xml中的資料
* <p>
* 擷取xml中的測試資料,封裝該資料可以用來測試外部提供資料進行批量插入,及查詢等的預期資料
* <p>
* 這個dbunit需要依賴poi3.2的包
*/
public void testXMLLoad() {
Map<String, Map<Integer, String>> map = loadLocalData("/com/um/vstore/portal/service/impl/note.xml");
if (map != null && map.size() != 0) {
Set<String> set = map.keySet();
for (String key : set) {
Map<Integer, String> rowMap = map.get(key);
if (rowMap != null && rowMap.size() != 0) {
Set<Integer> rowSet = rowMap.keySet();
for (Integer rowKey : rowSet) {
String row = rowMap.get(rowKey);
JSONObject obj = JSONObject.fromObject(row);
System.out.println(obj.getString("NOTES") + "||"
+ obj.getInt("PAYTYPE"));
}
} else {
assertTrue("表格[" + key + "]擷取行資料異常", false);
}
}
assertTrue("擷取行xml資料正常", true);
} else {
assertTrue("xml測試資料沒有擷取到值", false);
}
}
/**
* 加載本地excel或者xml得資料方法,用以提供預期資料
* <p>
* 該方法擷取本地excel或者xml得資料,用以提供測試用例資料,方法傳回一個以資料表為機關得map對象,key值為表名(sheet名),
* value為每個表得資料得map對象
* ,該map存儲了該表得所有資料,這個map以行為機關,key值為從1開始得行号,vaule為每行得資料,格式為json字元串
* ,如:{name:"umpay";age:10}
*
* @param dataPath
* 本地資料表格路徑,該位址是相對工程的絕對路徑,比如:/com/um/vstore/service/impl/note.
* xls
* @return 傳回一個以表為機關得map對象
*/
private Map<String, Map<Integer, String>> loadLocalData(String dataPath) {
DataFileLoader loader = null;
if (dataPath.endsWith(".xml")) {
loader = new FlatXmlDataFileLoader();
} else if (dataPath.endsWith(".xls")) {
loader = new XlsDataFileLoader();
}
if (loader != null) {
IDataSet ds = loader.load(dataPath);
try {
ITableIterator iterator = ds.iterator();
Map<String, Map<Integer, String>> tableMap = new HashMap<String, Map<Integer, String>>();
// 周遊有多少個表
while (iterator.next()) {
ITable table = iterator.getTable();
// 這裡的行數已經在table中被dbunit給去掉了頭,是以這裡傳回的行數是不包含标題頭的,它是真實資料的行數
int row = table.getRowCount();
if (row >= 1) {// 有資料才周遊
ITableMetaData tableMetaData = table.getTableMetaData();
String tableName = tableMetaData.getTableName();// 擷取表名,即sheet得名稱
// 擷取列名
Column[] columns = table.getTableMetaData()
.getColumns();
Map<Integer, String> rowMap = new HashMap<Integer, String>();
for (int i = 0; i < row; i++) {
JSONObject jsonRow = new JSONObject();
for (Column column : columns) {
String columnName = column.getColumnName();
// rowValue = String.valueOf(table.getValue(i,
// columnName));
jsonRow.put(columnName,
table.getValue(i, columnName));
rowMap.put(i + 1, jsonRow.toString());
}
}
tableMap.put(tableName, rowMap);
}
}
return tableMap;
} catch (DataSetException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 生成一個随機數作為id
*
* @return
*/
private String getId() {
StringBuffer id = new StringBuffer();
id.append(DateUtil.getDateAndTime());
Random random = new Random();
// 保證這個随機碼的位數是4位的
int num = random.nextInt(10000);
random.setSeed(111222333);
id.append(String.format("%04d", num));
return id.toString();
}
/**
* dbunit資料庫連接配接關閉
*/
private void closeConnection() {
if (conn != null) {
try {
conn.close();
conn = null;
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 通過dbunit擷取資料查詢的結果
* <p>
* 通過dbunit擷取資料查詢的結果,将結果封裝到DataSet中,便于和我們預期的值(自定義xml中資料)比較
*
* @param conn
* dbunit資料庫連接配接對象
* @param resultName
* 結果集名,要和自定義的預期xml中的标簽名一緻
* @param querySql
* 你想要的查詢的sql,可以為空,為空的時候是查的整個表,把整個表資料查出放到資料集中
* @return
*/
private QueryDataSet getQueryDataSet(IDatabaseConnection conn,
String resultName, String querySql) {
if (resultName == null || resultName == "") {
return null;
}
QueryDataSet actual = new QueryDataSet(conn);
try {
if (querySql != null && querySql != "") {
actual.addTable(resultName, querySql);
} else {
actual.addTable(resultName);
}
return actual;
} catch (AmbiguousTableNameException e) {
e.printStackTrace();
}
return null;
}
/**
* 擷取dbunit資料庫連接配接對象
*
* @param dataBaseName
* 資料庫名稱(schema)
* @return
*/
private IDatabaseConnection getDatabaseConnection(String dataBaseName) {
try {
IDatabaseConnection connection = new DatabaseConnection(
DataSourceUtils.getConnection(dataSource), "VSTORE");
return connection;
} catch (CannotGetJdbcConnectionException e) {
e.printStackTrace();
} catch (DatabaseUnitException e) {
e.printStackTrace();
}
return null;
}
/**
* 加載本地準備的xml資料
*
* @param xmlPath
* xml資料檔案位址,該位址是相對工程的絕對路徑,比如:/com/um/vstore/service/impl/
* mydata_xml.xml
* @return
*/
private IDataSet loadXMLDataSet(String xmlPath) {
if (xmlPath == null || xmlPath == "") {
return null;
}
if (!xmlPath.endsWith(".xml")) {
return null;
}
DataFileLoader loader = new FlatXmlDataFileLoader();
IDataSet ds = loader.load(xmlPath);
return ds;
}
@Test
public void testLoadXML() {
loadXMLDataSet("/com/um/vstore/portal/service/impl/paynote_xml.xml");
}
}