- 浏览: 2473235 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (574)
- Book (62)
- Architecture (6)
- Java (39)
- Taobao (41)
- Distributed (4)
- Life (72)
- Database (7)
- Spring (16)
- Photography (15)
- Bicycle (41)
- Test (20)
- jBPM (8)
- Business (12)
- Movie (3)
- Ajax (15)
- Code (7)
- Eclipse (96)
- VIM (2)
- Music (6)
- Groovy (10)
- AutoHotKey (3)
- Dorado (10)
- Maven (7)
- Scrum (5)
- English (20)
- Financial (12)
- OSGi (3)
- Other (4)
- Tool (6)
- Browser (1)
- PPT (1)
- Project Management (4)
- Agile (6)
- Nosql (1)
- Search engine (6)
- Shell (2)
- Open Source (4)
- Storm (10)
- Guava (3)
- Baby (1)
- netty (1)
- Algorithm (1)
- Linux (1)
- Python (2)
最新评论
-
roy2011a:
https://github.com/ebottabi/sto ...
storm的序列化问题及与spring的结合方式 -
roy2011a:
能抗能打 写道哥们儿,你好!能共享下那个storm与sprin ...
storm的序列化问题及与spring的结合方式 -
Alick1:
兄弟,你之前是不是在深圳的正阳公司呆过啊?
storm的ack和fail -
liuleixwd:
先点个赞,写的非常好!有个问题请教下,如果我再bolt里不用e ...
storm的ack和fail -
yao-dd:
solr的facet查询
最近在对unitils进行扩展, 主要是数据库这块儿的内容, 对, 就是dbunit, dbunit给我的感觉是扩展性太差了, 很多类的构造函数采用包可见, 抽象类的抽象方法包可见, 根本没办法继承复写某些方法, 可定制性和unitils比起来也差的不是一点点, 根本就是一个封闭的系统. 导致很多代码不得不大段的拷贝代码来满足自身的需要.
我这里采用了excel格式来构造测试数据, 目前发现的dbunit(使用版本为2.4.6)的几个问题:
针对以上问题的解决方案:
科学计数法问题
这里需要对XlsTable中的getValue()方法下进行处理, 具体代码如下:
这里暂定精确到小数5位, 有没有更好的解决方案?
日期时间问题
在某个地方对TimeZone做个初始化, 具体原理懒得去探究了, 反正我这样解决了问题
空行的问题
这个需要实现一个IBatchStatement, 在批量处理数据的时候, 如果遇到空行应该跳过, dbunit有一个自己的实现:BatchStatement, 日!把构造函数声明为包可见, 让你无从继承改写. 无奈copy一些代码重写了一个. 另外还需要重写一个PreparedStatementFactory, 用来创建自己的那个IBatchStatement实现类.
具体就是加了几个判断, 批量处理的行数据为null的时候不进行批量操作
其实dbunit也有一个类似unitils的控制中心:DatabaseConfig类. 但是可配置的东东太少太少, 我需要的没有, 有的我一个都不需要:(, 无语.
根据唯一键清数据
这个借鉴了dbunit原来的做法(现在去掉了唯一键标识的功能), 给字段加下划线style来标识. 本来打算根据数据库的metadata信息来获取唯一键的, 但是jdbc没有提供这样的接口, 而这种用下划线标识具有更好的扩展性, 可维护性, 可移植性.
具体做法是在XlsTable的createMetaData()方法中检查字段的style, 然后利用了Column中的remark属性来存储唯一键标识信息.
然后重新定义了一个DatabaseOperation:
dbunit中很多代码没法重用, 再一次祭出copy大法.
通过上面的步骤基本可以让unitils+dbunit能够满足目前项目的测试需要了.
我这里采用了excel格式来构造测试数据, 目前发现的dbunit(使用版本为2.4.6)的几个问题:
- 有些大数字会采用科学计数法来表示, 导致在解析的时候数据不正确
- dbunit内部对日期时间的处理会用到TimeZone这个东东, 这个在国际化方面应该是有价值的, 但是在我们的测试中却会导致时间与格林威治时间做8个小时的偏移转换
- 对excel中的空行没有进行处理(比如excel本来只有两行数据, 但是不知什么原因会存在一些空行, 导致在插入数据库的时候会有Null相关的错误)
- 对测试数据不仅涉及到根据主键清理, 有时候还需要根据一些特殊的字段值进行清理, 比如唯一键, 而这个需要进行扩展, 而XlsTable的包可见, 基本上必须另外实现一套(TMD,恨得让人直咬牙).
针对以上问题的解决方案:
科学计数法问题
这里需要对XlsTable中的getValue()方法下进行处理, 具体代码如下:
static final Pattern pattern = Pattern.compile("[eE]"); private BigDecimal toBigDecimal(double cellValue) { String resultString = String.valueOf(cellValue); // 针对科学计数法的处理(对于小数位数精确到5位) if (pattern.matcher(resultString).find()) { DecimalFormat format = new DecimalFormat("#####.#####"); resultString = format.format(cellValue); } if (resultString.endsWith(".0")) { resultString = resultString.substring(0, resultString.length() - 2); } BigDecimal result = new BigDecimal(resultString); return result; }
这里暂定精确到小数5位, 有没有更好的解决方案?
日期时间问题
在某个地方对TimeZone做个初始化, 具体原理懒得去探究了, 反正我这样解决了问题
// 由于dbunit对excel的时间处理会使用TimeZone.getOffset()做一个偏移转换, 这里需要设置一下 TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
空行的问题
这个需要实现一个IBatchStatement, 在批量处理数据的时候, 如果遇到空行应该跳过, dbunit有一个自己的实现:BatchStatement, 日!把构造函数声明为包可见, 让你无从继承改写. 无奈copy一些代码重写了一个. 另外还需要重写一个PreparedStatementFactory, 用来创建自己的那个IBatchStatement实现类.
public class TPreparedBatchStatement implements IPreparedBatchStatement { boolean notAllNull; // 所有参数是否为null boolean noParameter = true; // 是否指定参数 private int _index; ... public void close() throws SQLException { ... } public void addValue(Object value, DataType dataType) throws TypeCastException, SQLException { logger.debug("addValue(value={}, dataType={}) - start", value, dataType); noParameter = false; // Special NULL handling if (value == null || value == ITable.NO_VALUE) { _statement.setNull(++_index, dataType.getSqlType()); return; } notAllNull = true; dataType.setSqlValue(value, ++_index, _statement); } public void addBatch() throws SQLException { logger.debug("addBatch() - start"); // 没有参数, 或者有参数, 但是参数不全为null if (noParameter || (!noParameter && notAllNull)) { _statement.addBatch(); notAllNull = false; noParameter = true; } _index = 0; } ...
具体就是加了几个判断, 批量处理的行数据为null的时候不进行批量操作
其实dbunit也有一个类似unitils的控制中心:DatabaseConfig类. 但是可配置的东东太少太少, 我需要的没有, 有的我一个都不需要:(, 无语.
根据唯一键清数据
这个借鉴了dbunit原来的做法(现在去掉了唯一键标识的功能), 给字段加下划线style来标识. 本来打算根据数据库的metadata信息来获取唯一键的, 但是jdbc没有提供这样的接口, 而这种用下划线标识具有更好的扩展性, 可维护性, 可移植性.
具体做法是在XlsTable的createMetaData()方法中检查字段的style, 然后利用了Column中的remark属性来存储唯一键标识信息.
Column column = null; // 标识唯一键 byte underline = cell.getCellStyle().getFont(workbook).getUnderline(); if (underline == 1) { column = new Column(columnName, DataType.UNKNOWN, null, null, null, "unique", null); } else { column = new Column(columnName, DataType.UNKNOWN); } columnList.add(column);
然后重新定义了一个DatabaseOperation:
public class DeleteByUniqueKeyOperation extends DatabaseOperation { private static final Logger logger = LoggerFactory.getLogger(DeleteByUniqueKeyOperation.class); @Override public void execute(IDatabaseConnection connection, IDataSet dataSet) throws DatabaseUnitException, SQLException { logger.debug("execute(connection={}, dataSet={}) - start", connection, dataSet); IStatementFactory factory = getFactory(connection); // for each table ITableIterator iterator = dataSet.iterator(); while (iterator.next()) { ITable table = iterator.getTable(); // Do not process empty table if (isEmpty(table)) { continue; } List<String> uniqueKeys = getUniqueKeys(connection, table); if (uniqueKeys == null || uniqueKeys.size() == 0) { continue; } String sql = getSql(table, connection, uniqueKeys); executeSql(connection, factory, table, sql, uniqueKeys); } } private IStatementFactory getFactory(IDatabaseConnection connection) { DatabaseConfig databaseConfig = connection.getConfig(); IStatementFactory factory = (IStatementFactory) databaseConfig.getProperty(DatabaseConfig.PROPERTY_STATEMENT_FACTORY); return factory; } private void executeSql(IDatabaseConnection connection, IStatementFactory factory, ITable table, String sql, List<String> uniqueKeys) throws DatabaseUnitException, SQLException { IPreparedBatchStatement statement = null; ITableMetaData metaData = getOperationMetaData(connection, table.getTableMetaData()); Column[] columns = metaData.getColumns(); int count = table.getRowCount(); try { for (int i = 0; i < count; i++) { if (statement != null) { statement.executeBatch(); statement.clearBatch(); statement.close(); } statement = factory.createPreparedBatchStatement( sql, connection); addBatchValue(table, statement, columns, uniqueKeys, i); } statement.executeBatch(); statement.clearBatch(); } finally { if (statement != null) { statement.close(); } } } private void addBatchValue(ITable table, IPreparedBatchStatement statement, Column[] columns, List<String> uniqueKeys, int row) throws SQLException, DataSetException, TypeCastException { for (Column column : columns) { // 必须保证都大写 String columnName = column.getColumnName(); if (!uniqueKeys.contains(columnName)) { continue; } try { statement.addValue(table.getValue(row, columnName), column.getDataType()); } catch (TypeCastException e) { throw new TypeCastException("Error casting value for table '" + table.getTableMetaData().getTableName() + "' and column '" + columnName + "'", e); } } statement.addBatch(); } private String getSql(ITable table, IDatabaseConnection connection, List<String> uniqueKeys) throws SQLException { // 获取唯一键 String tableName = table.getTableMetaData().getTableName(); StringBuilder sql = new StringBuilder(); sql.append("delete ").append(tableName).append(" where "); boolean first = true; for (String uniqueKey : uniqueKeys) { if (first) { first = false; } else { sql.append(" and "); } sql.append(uniqueKey).append("=").append("? "); } return sql.toString(); } private List<String> getUniqueKeys(IDatabaseConnection connection, ITable table) throws SQLException, DataSetException { List<String> uniqueKeys = new ArrayList<String>(); // 一种实现方式 Column[] columns = table.getTableMetaData().getColumns(); for (Column column : columns) { if ("unique".equals(column.getRemarks())) { uniqueKeys.add(column.getColumnName()); } } return uniqueKeys; } static boolean isEmpty(ITable table) throws DataSetException { ... } static ITableMetaData getOperationMetaData(IDatabaseConnection connection, ITableMetaData metaData) throws DatabaseUnitException, SQLException { ... } }
dbunit中很多代码没法重用, 再一次祭出copy大法.
通过上面的步骤基本可以让unitils+dbunit能够满足目前项目的测试需要了.
发表评论
-
Effective Unit Test:代码面前并非人人平等
2012-02-05 15:38 1710这里的观点非常值得探讨, 所有的产品代码就像是一项投资, 有些 ... -
使用Guava Supplier Mock Datetime
2012-02-04 23:35 2806通过下面的例子了解Guava Supplier的用法.在做单元 ... -
对多线程进行测试
2011-07-02 15:31 1419在junit下, 很难对多线程的代码进行测试, 因此需要借助其 ... -
补习一下EasyMock
2010-04-28 09:18 5052最近有时间, 再温习一下easymock相关的东东 http ... -
结合使用Dummy, InjectTo, PartialMock的单元测试
2009-12-24 21:21 1485结合使用Dummy, InjectTo, PartialMoc ... -
使用代码方式运行指定单元测试类及方法
2009-12-17 08:57 1776记一下, 备忘 private void runT ... -
JunitCore
2009-12-15 01:03 0JUnitCore core = new JUnitCore( ... -
让Unitils实现excel下的多数据源支持
2009-12-03 21:02 3955说明:这里的多数据源需要利用spring中配置的多个DataS ... -
unitils中spring module初始化源码解读
2009-12-03 20:32 2659spring module的初始化还算简单, 但是熟悉里面的原 ... -
解决unitils中的dbunit使用spring中定义的多数据源
2009-12-02 20:07 3178最近在研究unitils, dbunit来适应目前的单元测试. ... -
Unitils 学习笔记
2009-11-27 19:44 7796源码部分 EasyMock相关的 有三个注解: Mock ... -
使用classloader自定义测试套件TestSuite
2009-10-21 19:04 2349junit自带了一个suite用来将多个test case放在 ... -
《单元测试之道Java版——使用JUnit》读书笔记
2009-06-09 23:06 3426这个可能是到目前为止 ... -
JunitRunner定制之模仿@Before对属性进行初始化
2009-02-12 21:54 2550在测试的过程中, 会用到一些模拟数据, 希望在每次调用测试方法 ... -
打造自己的单元测试容器——Junit Runner扩展举例
2009-02-05 23:40 6225最近对java的annotation和junit的Runner ... -
junit4源码浅析
2009-02-05 21:12 7354junit3和junit4是两个非常不同的版本, 不能简单的理 ... -
最近单元测试中写的一个Assert
2009-01-15 22:03 2924该类提供的assert方法主要针对数据库的单元测试中, 使用S ... -
用于生成mock模型对象Setter代码工具类
2009-01-12 10:35 1804最近写的一个工具类, 用来生成mock的javabean对象, ... -
EasyMock最佳实践
2008-12-20 20:43 30138推荐easymock入门贴: 窥探 ... -
JUnit 4新特性笔记
2008-12-11 08:47 2935测试任何可能的错误。 ...
相关推荐
在使用 DBUnit 时,有几个必需的 Jar 包是必不可少的,这些 Jar 包提供了 DBUnit 的核心功能以及与其交互所需的依赖。根据描述,这里有四个关键的 Jar 包: 1. **dbunit.jar**:这是 DBUnit 的主库,包含了所有的 ...
这使得我们可以验证数据库操作是否按预期工作,确保代码对数据库的修改是正确的。 使用DBUnit,我们可以创建一个数据集(通常为XML或CSV格式),该数据集包含用于初始化数据库的记录。在测试开始前,这些数据会被...
DBUnit 是一个开源的 Java 库,专门用于数据库测试,它是 xUnit 测试框架(如 JUnit)的一个扩展。在数据库驱动的项目中,DBUnit 可以帮助开发者确保数据库状态的一致性,使得测试更加可靠。DBUnit 2.4.7 版本包含了...
在版本 2.4.9 中,DBUnit 提供了一系列的功能,帮助开发者在进行单元测试时能够管理和操作数据库的数据状态。 DBUnit 的核心功能包括: 1. 数据初始化:在测试前,DBUnit 可以导入 CSV、XML 或 Excel 文件中的数据...
在使用 DbUnit 进行数据库测试时,用户需要连接到真实数据库,并拥有对数据库或者至少是其中的模式(schema)的独占访问权限,以免不同的开发人员同时运行测试时相互冲突。 DbUnit 的使用场景非常广泛,例如可以...
在描述中提到的"flash能过控制java来运用数据库",这可能是指通过Java的Flex或Adobe AIR接口,使Flash应用程序能够与Java后端进行交互,进而利用Dbunit对数据库进行操作。这样的组合可以让前端的富媒体应用具有强大...
dbunit-2.2.3..jar dbunit-2.4.2.jar dbunit-2.5.3.jar dbunit-2.7.0.jar 发现每个版本对JDK是有要求的,比如2.7 只能用于JDK1.8版本,所以整理好几个jar包挑选适合自己的
Dbunit是一款强大的Java库,它扩展了JUnit,为数据库提供了结构化数据的导入和导出,使得测试更加方便和可靠。下面将详细介绍如何在Spring中使用Dbunit进行Dao的集成单元测试。 首先,我们需要理解集成测试的概念。...
DBUnit 的主要功能在于帮助开发者在执行测试前后对数据库进行初始化和清理,确保每次测试都在一个已知的、干净的状态下开始。这样可以避免因为数据残留或依赖关系导致的测试不准确。以下是一些关于DBUnit 2.2的知识...
DBUnit是一个强大的工具,它允许开发者在测试用例之间对数据库进行"干净"的状态设置。这通常涉及到在每个测试之前清除数据,然后插入特定的测试数据。DBUnit通过使用XML或Flat CSV格式的数据集来管理这些操作,使得...
DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类...
DBUnit 是一个开源的 Java 库,专门用于数据库测试,它是 xUnit 测试框架(如 JUnit)的一个扩展。在数据库驱动的项目中,DBUnit 提供了一种结构化的方法来设置和验证数据库状态,从而确保测试的一致性和可靠性。这...
标题“dbunit测试demo”表明这是一个关于如何使用 DBUnit 进行测试的实例。描述中提到的链接指向了一篇博客文章,这可能包含了一个具体的 DBUnit 测试用例的详细步骤和解释。尽管我们无法直接访问该链接,但我们可以...
DbUnit 是一个针对数据库驱动项目的JUnit扩展,同时也可用于Ant构建工具。它的主要功能是能够将数据库数据导出到XML数据集,并从XML数据集中导入,从而支持数据库的测试和数据管理。DbUnit还允许你验证数据库中的...
这样,即使测试对数据库进行了修改,也可以在测试结束后恢复原始状态。此外,DbUnit还支持各种操作,如比较数据库的实际状态与期望状态,以验证数据的正确性。 3. HttpUnit: HttpUnit是一个用于Web应用模拟客户端...
DBUnit 是一个强大的Java库,专门用于数据库的测试。它扩展了JUnit,使得数据库相关的单元测试变得更加简单和规范。在给定的压缩包文件中,我们有两个版本的DBUnit源码:`dbunit-2.0-src` 和 `dbunit-2.1-src`。这些...
在软件开发中,测试是保证代码质量和功能正确性的重要环节,而 DBUNIT 尤其适用于对数据库操作进行测试,特别是在进行批量数据处理时。 批量对比测试在数据库测试中扮演着关键角色,因为往往需要确保大量的数据导入...