基于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");
}
}