- 浏览: 2469765 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (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 1703这里的观点非常值得探讨, 所有的产品代码就像是一项投资, 有些 ... -
使用Guava Supplier Mock Datetime
2012-02-04 23:35 2801通过下面的例子了解Guava Supplier的用法.在做单元 ... -
对多线程进行测试
2011-07-02 15:31 1414在junit下, 很难对多线程的代码进行测试, 因此需要借助其 ... -
补习一下EasyMock
2010-04-28 09:18 5046最近有时间, 再温习一下easymock相关的东东 http ... -
结合使用Dummy, InjectTo, PartialMock的单元测试
2009-12-24 21:21 1482结合使用Dummy, InjectTo, PartialMoc ... -
使用代码方式运行指定单元测试类及方法
2009-12-17 08:57 1770记一下, 备忘 private void runT ... -
JunitCore
2009-12-15 01:03 0JUnitCore core = new JUnitCore( ... -
修改dbunit的几个bug兼对dbunit进行扩展
2009-12-08 20:51 3194最近在对unitils进行扩展, 主要是数据库这块儿的内容, ... -
让Unitils实现excel下的多数据源支持
2009-12-03 21:02 3946说明:这里的多数据源需要利用spring中配置的多个DataS ... -
unitils中spring module初始化源码解读
2009-12-03 20:32 2656spring module的初始化还算简单, 但是熟悉里面的原 ... -
Unitils 学习笔记
2009-11-27 19:44 7789源码部分 EasyMock相关的 有三个注解: Mock ... -
使用classloader自定义测试套件TestSuite
2009-10-21 19:04 2341junit自带了一个suite用来将多个test case放在 ... -
《单元测试之道Java版——使用JUnit》读书笔记
2009-06-09 23:06 3420这个可能是到目前为止 ... -
JunitRunner定制之模仿@Before对属性进行初始化
2009-02-12 21:54 2544在测试的过程中, 会用到一些模拟数据, 希望在每次调用测试方法 ... -
打造自己的单元测试容器——Junit Runner扩展举例
2009-02-05 23:40 6218最近对java的annotation和junit的Runner ... -
junit4源码浅析
2009-02-05 21:12 7350junit3和junit4是两个非常不同的版本, 不能简单的理 ... -
最近单元测试中写的一个Assert
2009-01-15 22:03 2917该类提供的assert方法主要针对数据库的单元测试中, 使用S ... -
用于生成mock模型对象Setter代码工具类
2009-01-12 10:35 1800最近写的一个工具类, 用来生成mock的javabean对象, ... -
EasyMock最佳实践
2008-12-20 20:43 30116推荐easymock入门贴: 窥探 ... -
JUnit 4新特性笔记
2008-12-11 08:47 2930测试任何可能的错误。 ...
相关推荐
unitils整合dbunit利用excel进行单元测试 包含mock以及整合spring进行测试
下面将详细介绍如何在Spring中使用Dbunit进行Dao的集成单元测试。 首先,我们需要理解集成测试的概念。集成测试是在所有模块单独通过单元测试后,将它们组合在一起进行的测试,目的是检查模块间的交互是否正确。在...
标题“使用Unitils测试DAO”涉及的是在Java开发中如何利用Unitils库来高效地测试数据访问对象(DAO)层的代码。Unitils是一个强大的、易于使用的集成测试框架,它简化了诸如数据库、ORM(对象关系映射)框架如...
2. **引入DBUnit依赖**:在项目的`pom.xml`或`build.gradle`文件中添加DBUnit的依赖,这样就可以在测试类中使用DBUnit的功能。 3. **设置数据源**:在Spring配置文件中定义一个数据源,用于连接测试数据库。确保该...
对于依赖注入,Unitils能够轻松地将Spring管理的Bean注入到单元测试中,允许在测试中使用Spring容器中的SessionFactory。它也简化了Mock对象的创建,特别是在使用EasyMock时,通过反射参数匹配使得Mock对象的使用...
用户可以在自己的测试类中直接或间接调用 DbUnit,或者从 Ant 中使用 DbUnit 来执行某些任务。 DbUnit 的优点是提供了一种相对简单灵活的方式来准备和验证数据库,这种方式独立于被测代码。 DbUnit 还可以与 JUnit ...
【dbunit-spring-demo】是一个基于Java的项目,它展示了如何在Spring框架中有效地使用DBUnit工具进行数据库测试。DBUnit是JUnit的一个扩展,专门用于数据库的集成测试,它允许开发者在测试之前填充数据库,执行测试...
通过集成 DBUnit,Unitils 可以帮助我们在测试中管理和操作数据库数据,确保每个测试用例都在干净的环境中运行。`pom.xml` 文件中的依赖可能包含了 DBUnit 的配置,使得我们可以在测试中导入和导出数据,进行数据...
在提供的文件列表中,我们可以看到 student_mysql.sql(可能是 SQL 导出的数据库备份),以及几种不同类型的 XML 文件(student_nonflat.xml、student_exp.xml、student_flat.xml、student_pre.xml),这些都是 ...
这个例子展示了如何在测试类中设置数据库连接,加载预期数据,然后在测试方法中使用 `CLEAN_INSERT` 操作清理并插入数据,最后验证数据库中的数据是否与预期相符。 ### 常见问题 1. **数据集格式错误**: 确保数据...
这些示例可能包括如何配置DBUnit,定义数据格式,以及如何在测试前后触发数据操作的代码片段。通过阅读和理解这些代码,开发者可以更好地掌握DBUnit的实际应用。 总的来说,DBUnit是数据库测试领域的一个强大工具,...
1. **准备数据**:使用 DBUnit 导入 XML 文件或 Excel 表格中的测试数据到数据库。 2. **执行测试**:在测试方法中,调用 DBUnit 的 API 运行测试,比如使用 `IDatabaseConnection` 和 `IDataSet` 接口。 3. **验证...
首先,为了在 Spring Boot 项目中使用 DBUnit,我们需要添加 DBUnit 的依赖。这可以通过在 `build.gradle` 或 `pom.xml` 文件中引入相应的坐标来完成。例如,在 Gradle 中,可以添加如下依赖: ```groovy ...
DBUnit 是一个开源的 Java 库,专门用于数据库测试,它是 xUnit 测试框架的一部分,提供了数据驱动测试的解决方案。在版本 2.4.9 中,DBUnit 提供了一系列的功能,帮助开发者在进行单元测试时能够管理和操作数据库的...
DbUnit提供了两种实现方式:`DatabaseConnection` 包装了JDBC连接,而 `DatabaseDataSourceConnection` 则包装了JDBC的数据源。这两个类使得DbUnit可以操作数据库并执行相关的数据操作。 **IDataSet** 接口表示一个...
2. Spring 集成:如果你的项目使用 Spring,可以利用 Spring's `DbUnitTestExecutionListener` 来自动管理 DBUnit 的生命周期。 3. Maven 插件:也有 Maven 插件可用来自动化 DBUnit 测试,例如 maven-dbunit-...
src 文件夹通常包含源代码,尽管在这个压缩包中没有详细列出其内容,但在实际的Dbunit项目中,src文件夹下会有Java源代码、测试代码以及相关的配置文件。 doc 文件夹可能包含了Dbunit的API文档或者其他技术文档,...
数据集是 DBUnit 的基础,它通常是一个 XML 文件或者 CSV 文件,描述了测试用例中的预期数据库状态。这些文件包含了表的行和列,模拟了数据库中的数据。操作模式则定义了如何将数据集加载到数据库,比如 `CLEAN_...
- 在这里,`dbunit` 任务同样指定数据库连接属性,但操作类型为 `export`,将数据导出到 `data.xml` 文件中。 - `<query>` 和 `<table>` 标签用于指定要导出的特定查询和表。 3. **操作类型(Operation Types)**...