- 浏览: 2446761 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (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来适应目前的单元测试.
在unitils中将要使用的数据源都定义在unitils.properties中, 而在我们的测试配置中, 同样的数据源也在spring中配置了一份儿, 因此本人希望同样的配置不应该出现在两个方面, 从而增加维护成本. 另一方面还可以通过spring来解决多数据源的问题. 于是开始看unitils的源代码, 原来在dbunit module中, 获取数据源的代码:
也就是说, 最终的数据源是通过DatabaseModule来获取的, 因此我们需要对这些代码进行改造, 但是还有另外一个问题, unitils的spring module对所有测试的spring context都是按照以测试类为key, context为value进行缓存的. 因此要获取spring的context, 必须知道当前的测试类, 因此这里需要整体调整Dbunit module的insertDataset()方法的内部方法调用的参数. 在处理流程中增加testObject参数. 这个需要修改的代码非常多, 这里不一一展开, 对于从spring中获取数据源的代码如下:
为了将spring的datasource与dbunit能够关联起来, 本人对构造的测试数据做了一个约定, 对于操作的数据表, 必须加数据源标识前缀, 而数据源标识则对应spring bean配置文件中的id, 比如配置了一个datasource bean:
如果有一个dataset中有一个table(table1)跟该datasource关系, 那么该表名必须这样定义: db1.table1. 也可以将db1前缀理解为schema(但这里实际并不是).对于这个问题, 已经跟unitils的founder进行了交流, 并在jira中增加了一个issue: http://jira.unitils.org/browse/UNI-190 在未来版本中将实现该功能.
在对数据清理的处理上的改进
对于构造的测试准备数据的清理, dbunit在默认情况下, 假定所有测试的数据库表必须建立主键, 否则会抛出异常, 因为在清理数据的时候需要利用主键以及构造的对应主键值来做db的delete操作.而我们目前存在一些关联表是没有主键的, 因此给使用unitils带来了一定的麻烦. 但也是可以搞定的. 另外一个问题就是, 对于所有的构造数据都会根据dataset中定义的table来处理, 而目前我们面临的另外一个问题, 就是在测试前, 希望能通过指定的sql来对测试环境对某些数据进行清理操作, 以避免对测试造成影响. 于是对unitils的DataSetLoadStrategy和dbunit的DatabaseOperation进行了扩展.
本人定义了一个ExecuteSqlOperation用来执行指定的sql语句:
从代码中我们可以看出, dbunit中的DatabaseOperation 与dataset是有非常强的耦合的(本来嘛, dataset是整个dbunit的模型核心), 而这里我们对dataset是不需要的: 拿到connection, 执行sql, 退出.
然后定义了一个CustemAndInsertDataSetLoadStrategy, 用来引入我们上面定义的这个ExecuteSqlOperation :
在实际测试中使用:
当然中间省略了一些实现细节, 发现经过这样扩展之后, 基本能满足我们的测试需要.
感觉多数据源的使用是一个比较少的场景, 从dbunit, unitils的feature中就可以看出来, 貌似二者基本上没有做实现, 因此有了上面的代码.
在unitils中将要使用的数据源都定义在unitils.properties中, 而在我们的测试配置中, 同样的数据源也在spring中配置了一份儿, 因此本人希望同样的配置不应该出现在两个方面, 从而增加维护成本. 另一方面还可以通过spring来解决多数据源的问题. 于是开始看unitils的源代码, 原来在dbunit module中, 获取数据源的代码:
public DbUnitDatabaseConnection getDbUnitDatabaseConnection(String schemaName) { DbUnitDatabaseConnection dbUnitDatabaseConnection = dbUnitDatabaseConnections.get(schemaName); if (dbUnitDatabaseConnection == null) { dbUnitDatabaseConnection = createDbUnitConnection(schemaName); dbUnitDatabaseConnections.put(schemaName, dbUnitDatabaseConnection); } return dbUnitDatabaseConnection; } protected DbUnitDatabaseConnection createDbUnitConnection(String schemaName) { // A DbSupport instance is fetched in order to get the schema name in correct case DataSource dataSource = getDatabaseModule().getDataSourceAndActivateTransactionIfNeeded(); ... }
也就是说, 最终的数据源是通过DatabaseModule来获取的, 因此我们需要对这些代码进行改造, 但是还有另外一个问题, unitils的spring module对所有测试的spring context都是按照以测试类为key, context为value进行缓存的. 因此要获取spring的context, 必须知道当前的测试类, 因此这里需要整体调整Dbunit module的insertDataset()方法的内部方法调用的参数. 在处理流程中增加testObject参数. 这个需要修改的代码非常多, 这里不一一展开, 对于从spring中获取数据源的代码如下:
private DataSource getDataSource(String schemaName, Object testObject) { // 从spring中取 SpringModule springModule = Unitils.getInstance().getModulesRepository().getModuleOfType(SpringModule.class); DataSource dataSource = (DataSource) springModule.getSpringBean(testObject, schemaName); if (dataSource == null) { throw new RuntimeException(String.format("datasource[%s]在spring配置文件中不存在", schemaName)); } return dataSource; }
为了将spring的datasource与dbunit能够关联起来, 本人对构造的测试数据做了一个约定, 对于操作的数据表, 必须加数据源标识前缀, 而数据源标识则对应spring bean配置文件中的id, 比如配置了一个datasource bean:
<bean id="db1" parent="parentDataSource"> <property name="url"> <value>jdbc:oracle:oci:@${db1.name}</value> </property> <property name="username"> <value>${db1.username}</value> </property> <property name="password"> <value>${db1.password}</value> </property> </bean>
如果有一个dataset中有一个table(table1)跟该datasource关系, 那么该表名必须这样定义: db1.table1. 也可以将db1前缀理解为schema(但这里实际并不是).对于这个问题, 已经跟unitils的founder进行了交流, 并在jira中增加了一个issue: http://jira.unitils.org/browse/UNI-190 在未来版本中将实现该功能.
在对数据清理的处理上的改进
对于构造的测试准备数据的清理, dbunit在默认情况下, 假定所有测试的数据库表必须建立主键, 否则会抛出异常, 因为在清理数据的时候需要利用主键以及构造的对应主键值来做db的delete操作.而我们目前存在一些关联表是没有主键的, 因此给使用unitils带来了一定的麻烦. 但也是可以搞定的. 另外一个问题就是, 对于所有的构造数据都会根据dataset中定义的table来处理, 而目前我们面临的另外一个问题, 就是在测试前, 希望能通过指定的sql来对测试环境对某些数据进行清理操作, 以避免对测试造成影响. 于是对unitils的DataSetLoadStrategy和dbunit的DatabaseOperation进行了扩展.
本人定义了一个ExecuteSqlOperation用来执行指定的sql语句:
public class ExecuteSqlOperation extends DatabaseOperation { private Map<String, List<String>> sqlMap; private static final Logger logger = LoggerFactory.getLogger(ExecuteSqlOperation.class); public ExecuteSqlOperation(Map<String, List<String>> sqlMap) { this.sqlMap = sqlMap; } @Override public void execute(IDatabaseConnection connection, IDataSet dataSet) throws DatabaseUnitException, SQLException { logger.debug("execute(connection={}, dataSet={}) - start", connection, dataSet); DatabaseConfig databaseConfig = connection.getConfig(); IStatementFactory factory = (IStatementFactory) databaseConfig.getProperty(DatabaseConfig.PROPERTY_STATEMENT_FACTORY); // for each table Iterator<Entry<String, List<String>>> iterator = sqlMap.entrySet().iterator(); while (iterator.hasNext()) { Entry<String, List<String>> sqlEntry = iterator.next(); String schema = sqlEntry.getKey(); // Do not process empty table if (StringUtils.isBlank(schema) || sqlEntry.getValue().isEmpty()) { logger.warn(String.format("schema{%s} or sqls(%s) is empty", schema, sqlEntry.getValue())); continue; } // don't find the datasource if (!connection.getSchema().equalsIgnoreCase(schema)) { continue; } IPreparedBatchStatement statement = null; try { for (String sql : sqlEntry.getValue()) { statement = factory.createPreparedBatchStatement(sql, connection); statement.addBatch(); } statement.executeBatch(); statement.clearBatch(); // clear schema and sql iterator.remove(); } finally { if (statement != null) { statement.close(); } } } } public void setSqlMap(Map<String, List<String>> sqlMap) { this.sqlMap = sqlMap; } }
从代码中我们可以看出, dbunit中的DatabaseOperation 与dataset是有非常强的耦合的(本来嘛, dataset是整个dbunit的模型核心), 而这里我们对dataset是不需要的: 拿到connection, 执行sql, 退出.
然后定义了一个CustemAndInsertDataSetLoadStrategy, 用来引入我们上面定义的这个ExecuteSqlOperation :
public abstract class CustemAndInsertDataSetLoadStrategy implements DataSetLoadStrategy { public void execute(DbUnitDatabaseConnection dbUnitDatabaseConnection, IDataSet dataSet) { try { doExecute(dbUnitDatabaseConnection, dataSet); DatabaseOperation.INSERT.execute(dbUnitDatabaseConnection, dataSet); } catch (DatabaseUnitException e) { throw new UnitilsException("Error while executing DataSetLoadStrategy", e); } catch (SQLException e) { throw new UnitilsException("Error while executing DataSetLoadStrategy", e); } } abstract protected void doExecute(DbUnitDatabaseConnection dbUnitDatabaseConnection, IDataSet dataSet) throws DatabaseUnitException, SQLException; }
在实际测试中使用:
@DataSet(loadStrategy = MyTest.MyDataSetLoadStrategy.class) public class MyTest extends IcBaseCase2 { public static class MyDataSetLoadStrategy extends CustemAndInsertDataSetLoadStrategy { private Map<String, List<String>> sqlMap = new HashMap<String, List<String>>(); private ExecuteSqlOperation executeSqlOperation; public SpuRelationDataSetLoadStrategy() { sqlMap.put("db1", Collections .singletonList("delete table1 where id = 110")); executeSqlOperation = new ExecuteSqlOperation(sqlMap); } @Override protected void doExecute(DbUnitDatabaseConnection dbUnitDatabaseConnection, IDataSet dataSet) throws DatabaseUnitException, SQLException { executeSqlOperation.execute(dbUnitDatabaseConnection, dataSet); } }
当然中间省略了一些实现细节, 发现经过这样扩展之后, 基本能满足我们的测试需要.
感觉多数据源的使用是一个比较少的场景, 从dbunit, unitils的feature中就可以看出来, 貌似二者基本上没有做实现, 因此有了上面的代码.
发表评论
-
Effective Unit Test:代码面前并非人人平等
2012-02-05 15:38 1656这里的观点非常值得探讨, 所有的产品代码就像是一项投资, 有些 ... -
使用Guava Supplier Mock Datetime
2012-02-04 23:35 2752通过下面的例子了解Guava Supplier的用法.在做单元 ... -
对多线程进行测试
2011-07-02 15:31 1367在junit下, 很难对多线程的代码进行测试, 因此需要借助其 ... -
补习一下EasyMock
2010-04-28 09:18 5016最近有时间, 再温习一下easymock相关的东东 http ... -
结合使用Dummy, InjectTo, PartialMock的单元测试
2009-12-24 21:21 1447结合使用Dummy, InjectTo, PartialMoc ... -
使用代码方式运行指定单元测试类及方法
2009-12-17 08:57 1724记一下, 备忘 private void runT ... -
JunitCore
2009-12-15 01:03 0JUnitCore core = new JUnitCore( ... -
修改dbunit的几个bug兼对dbunit进行扩展
2009-12-08 20:51 3167最近在对unitils进行扩展, 主要是数据库这块儿的内容, ... -
让Unitils实现excel下的多数据源支持
2009-12-03 21:02 3910说明:这里的多数据源需要利用spring中配置的多个DataS ... -
unitils中spring module初始化源码解读
2009-12-03 20:32 2624spring module的初始化还算简单, 但是熟悉里面的原 ... -
Unitils 学习笔记
2009-11-27 19:44 7733源码部分 EasyMock相关的 有三个注解: Mock ... -
使用classloader自定义测试套件TestSuite
2009-10-21 19:04 2306junit自带了一个suite用来将多个test case放在 ... -
《单元测试之道Java版——使用JUnit》读书笔记
2009-06-09 23:06 3384这个可能是到目前为止 ... -
JunitRunner定制之模仿@Before对属性进行初始化
2009-02-12 21:54 2499在测试的过程中, 会用到一些模拟数据, 希望在每次调用测试方法 ... -
打造自己的单元测试容器——Junit Runner扩展举例
2009-02-05 23:40 6178最近对java的annotation和junit的Runner ... -
junit4源码浅析
2009-02-05 21:12 7312junit3和junit4是两个非常不同的版本, 不能简单的理 ... -
最近单元测试中写的一个Assert
2009-01-15 22:03 2891该类提供的assert方法主要针对数据库的单元测试中, 使用S ... -
用于生成mock模型对象Setter代码工具类
2009-01-12 10:35 1769最近写的一个工具类, 用来生成mock的javabean对象, ... -
EasyMock最佳实践
2008-12-20 20:43 30034推荐easymock入门贴: 窥探 ... -
JUnit 4新特性笔记
2008-12-11 08:47 2896测试任何可能的错误。 ...
相关推荐
unitils整合dbunit利用excel进行单元测试 包含mock以及整合spring进行测试
NULL 博文链接:https://xiangxingchina.iteye.com/blog/1752521
dbunit-spring-demo DBUnit Utility演示(使用Spring)
DBUNIT使用的详细文档
DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类DBUNIT 基类...
dbunit使用必需Jar包,总共4个必需Jar包
Unitils 构建在DbUnit 与 EasyMock 项目之上并与JUnit 和TestNG 相结合。支持数据库测试,支持利用Mock 对象 进行测试并提供与Spring 和Hibernate 相集成。Unitils 设计成以一种高度可配置和松散耦 合的方式来添加...
文档是关于DBUnit的ant的使用文档,通俗易懂
12.2 在Spring中使用Hibernate 12.2.1 配置SessionFactory 12.2.2 使用HibernateTemplate 12.2.3 处理LOB类型数据 12.2.4 添加Hibernate事件监听器 12.2.5 使用原生Hibernate API 12.2.6 使用注解配置 12.2.7 事务...
介绍了DBUNIT最新版的使用方法。并给出了一个详细的例子
用dbunit的两个例子,很不错,有帮助
在最近的一个项目中,我尝试使用用DBUnit对Spring+iBatis的架构进行测试,下面记录了DBUnit的使用过程和遇到的一些问题。首先,我们建立一个测试环境(基于Maven2和Oracle数据库*)。数据表名Account。先建立一个...
自述文件这个 repo 是我尝试使用 Spring Test DBUnit 实现的示例。 基本上我在 src/main/java/com/springtests/model 中有三个实体节目表演者每个都有自己的 ID 字段,并与具有三列的连接表 (prog_performer) 建立多...
介绍dbunit的使用和原理,核心组件介绍
12.2 在Spring中使用Hibernate 12.2.1 配置SessionFactory 12.2.2 使用HibernateTemplate 12.2.3 处理LOB类型数据 12.2.4 添加Hibernate事件监听器 12.2.5 使用原生Hibernate API 12.2.6 使用注解配置 12.2.7 事务...
NULL 博文链接:https://xiongzhenhui.iteye.com/blog/802984
Dbunit 基本原理就是在跑测试用例运行之前对数据表做用户定义的操作,清空不想要的数据,插入用户自定义的数据,使得该数据表处于用户知道的一种状态。而用户自定义的数据使用项目里的一个 xml 文件来表示。 Xml ...
单元测试框架Unitils例子工程,集成了DBUnit,EasyMock.
主要介绍了Spring Boot 与DBunit 配合使用方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧