通过H2+Dbunit+JUnit进行数据库相关的测试
很多时候我们在编写测试时,常常需测试一些需要访问数据库的功能,在处理这样的测试时,为了保证测试可模拟测试场景、可重复执行,我们需要一个简单容易的数据准备工具;一个隔离的干净的数据库环境。
为了达到以上目标,可以通过许多方式达到,比如同事爱用的用各种Model的Builder+数据库事务回滚(跨库问题),和有些人爱用的每次执行前都清掉数据库然后Dbunit(如果没有自己独立的库往往影响其他同事工作)。
最后自己选择的方法是结合builder(准备待插入数据)和Dbunit,然后使用H2数据库并开启内存模式和MySQL兼容模式来处理与数据库相关的测试。(是的,学习了Spring-Side的测试方法(*^__^*) ),这样每次测试时都可以有个相当干净的环境,并且不需要为了跑个测试单独建库建表。
对于Dbunit、H2的具体情况请查看相关文档,这里就不做细述,下面就来看下如何结合H2\Dbunit进行测试。
首先,我们需要准备H2数据库。既然H2是“内嵌式”数据库,自然不需传统数据库“安装”过程,如果您使用maven,要做的仅仅是加入依赖包
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.3.160</version> <scope>test</scope> </dependency>
就可以在代码中直接连接h2并使用了。
接下来要做的是并获取他的数据源以进行接下来的操作。
这里根据项目使用的不通技术可以有不同选择,比如项目使用了spring,那可以使用Spring Embedded database support(http://static.springsource.org/spring/docs/3.0.0.M4/spring-framework-reference/html/ch12s08.html)
<jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:schema.sql"/> <jdbc:script location="classpath:test-data.sql"/> </jdbc:embedded-database>
然后dataSource就被你所拥有…但这个在使用过程中发现个问题就是通过使用embedded-database标签没法自己定义连接URL开启MySQL模式,且因为项目使用mybatis没法像Hibernate之类的ORM容易处理sql语法不匹配问题,最后没有选择使用。
如需要通过使用spring管理配置数据源,可以考虑自己实现DataSourceFactoryBean,可以配置URL、用户名、密码和建表脚本路径,然后在bean初始化时通过配置的建表语句建表…
接下来要说的是没有使用spring的情况,这时使用老办法,直接连接数据库。
public final class H2DatabaseUtil { private static final Logger logger = LoggerFactory.getLogger(H2DatabaseUtil.class);
private H2DatabaseUtil() { }
public static DataSource getDataSource() { if (logger.isDebugEnabled()) { logger.debug("get H2 data source."); } SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriverClass(org.h2.Driver.class); dataSource.setUsername("sa"); dataSource.setPassword(""); dataSource.setUrl("jdbc:h2:mem:testdb;MODE=MYSQL;DB_CLOSE_DELAY=-1"); return dataSource; }
public static void initSchema(DataSource ds) { if (logger.isDebugEnabled()) { logger.debug("start init schema."); } try { QueryRunner queryRunner = new QueryRunner(ds); queryRunner.update("drop all objects"); if (logger.isDebugEnabled()) { logger.debug("delete all object k.o."); } try { queryRunner.update("runscript from '" + new DefaultResourceLoader() .getResource("schema.sql").getURL().toString() + "'"); if (logger.isDebugEnabled()) { logger.debug("rebuild the schema finish! f~resh meat..."); } } catch (IOException e) { logger.error(e.getMessage(), e); } } catch (SQLException e) { logger.error(e.getMessage(),e); } } }
这里使用了DbUtils来简化建表语句的执行,至于DataSource类型的选择,可以根据情况自己更换。
然后可以创建一个Test的基类让测试代码方便使用dataSource
public abstract class AbstractDBAwareTestCase { protected QueryRunner queryRunner ; protected DataSource dataSource;
protected void setUp() throws Exception { dataSource = getDataSource(); initSchema(dataSource); queryRunner = new QueryRunner(dataSource); } }
之后写的单元测试只需继承此类就可以获取已建好库的数据进行测试。
在得到数据源后,我们要做的操作是用Dbunit向这个崭新的数据库里头灌入数据。
觉得Dbunit还是不够好用?这里推荐直接copy更改spring-side的Fixtures类
(http://springside.googlecode.com/svn/springside4/trunk/modules/core/src/main/java/org/springside/modules/test/data/Fixtures.java)
这个类有个问题是需要对spring的resource相关类有依赖(获取classpath下的文件之类的),如果有问题还需更改下。
最后看个简单Test的使用列子:
public class AgentRepositoryTest extends AbstractDBAwareTestCase { private IAgentRepository agentRepository;
@Before public void setUp() throws Exception { super.setUp(); reloadAllTable(dataSource, "/data/agent-data.xml"); agentRepository = Lookup.getDefault().lookup(IAgentRepository.class); }
@Test public void should_find_agent_by_id() throws Exception { Agent agent = agentRepository.findById(dataSource, 1); assertNotNull(agent); assertThat(agent.getSimpleName(), is("demoa")); } }
ps:还需要注意的是虽然使用H2的MySQL模式,但有的语句还是不兼容,比如建表语句中的comment\innodb\tinyint可能都需要稍作调整。