天天看点

spring test+junit+dbunit单元测试示例

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

继续阅读