单元测试对提高程序质量的作用毋庸置疑, 本文将用实际的例子来介绍如何测试面向数据库的应用程序。 刚接触单元测试的同学常常会疑惑什么东西需要测试什么东西不需要测试, 而对于面向数据库的程序, 我们的主要逻辑往往是在sql上,我们写了很多sql语句来完成应用需要的操作, sql语句执行是否正确和应用是否正确关联性极强。 对于这类程序我们可以用dbunit来做测试。
本文介绍如何使用dbunit来测试spring + mybatis这种访问数据库的框架组合。
我用一个简单的demo来介绍如何做数据库的单元测试,项目的结构如下:
下面我逐个介绍项目中涉及dbunit单元测试的部分, 首先是我们新建了一个maven项目,要在此项目中做dbunit的单元测试,我们需要在pom文件中引用spring-test, junit, dbunit,以及h2相关的maven依赖, spring-test用来加载spring配置文件,h2用来模拟mysql执行sql语句,这里我们不用mysql或者其他数据库,而是用内存数据库h2,这是出于两个原因考虑的,一是h2是内存数据库,测试执行起来更快; 二是h2是java内置数据库,不管是在什么环境下,只要有java,测试用例就可以跑起来, 不必连真正的数据库。
pom.xml文件中添加的单元测试相关依赖项如下:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.dbunit</groupId> <artifactId>dbunit</artifactId> <version>${dbunit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>${h2.version}</version> <scope>test</scope> </dependency>
下一步我们定义了NoteDao接口,在这个接口中有对Note表相关的添查改删操作的方法, 然后我们在NoteMapper中写了四个方法对应的sql语句,这些内容和我们平时使用mybatis没什么两样,这里不做详述。
我们在src/test/java中添加NoteDaoTest类,用来测试NoteDao类,此类的内容如下,请注意看注释:
package hello; import cn.outofmemory.dbunit.dao.NoteDao; import cn.outofmemory.dbunit.entity.Note; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.Date; /** * NoteDao实现的测试类 * * 这个类必须有@RunWith(SpringJUnit4ClassRunner.class)直接指定使用spring测试, @ContextConfiguration 注解指定要使用此单元测试类需要预先加载的spring配置文件 * * Created by yukaizhao on 2015/10/26. */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"/spring/spring-dao.xml"}) public class NoteDaoTest { /** * 使用@Autowired注解自动注入NoteDao测试类 */ @Autowired private NoteDao noteDao; /** * 测试NoteDao的insert方法, 此处我们构造了一个Note对象,然后将起insert到数据库 */ @Test public void insertTest() { Note note = new Note(); note.setTitle("test title"); note.setContent("test content"); note.setCreateTime(new Date()); noteDao.insert(note); //此处通过验证note的id属性是否为正整数来验证插入是否成功 Assert.assertTrue(note.getId() > 0); } /** * 测试更新操作 */ @Test public void updateTest() { Note note = new Note(); note.setId(1); String newTitle = "new Title"; note.setTitle(newTitle); String newContent = "new Content"; note.setContent(newContent); int effectRows = noteDao.update(note); //此处通过update操作返回的受影响行数来断定update操作是否执行成功 Assert.assertTrue (effectRows == 1); } /** * 测试delete操作 */ @Test public void deleteTest() { int wantDeleteId = 2; int effectRows = noteDao.delete(wantDeleteId); //通过验证受影响行数来断定是否成功执行操作 Assert.assertEquals(1, effectRows); }
/** * 测试select操作 */ @Test public void selectTest() { int selectId = 3; Note note = noteDao.selectNoteById(selectId); //通过select出的note的id属性来断言是否成功 Assert.assertEquals(selectId, note.getId()); } }
这里的所有测试都需要初始化数据库,并且在数据库中初始化一些数据来做测试操作,这个初始化的操作,是在src/test/resources/spring/spring-dao.xml文件中定义的,我们看下spring-dao.xml文件中测试数据源定义和测试数据初始化bean定义的内容:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="org.h2.Driver" /> <property name="url" value="jdbc:h2:mem:bone;DB_CLOSE_DELAY=-1;MODE=MySQL" /> </bean> <jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS"> <jdbc:script location="classpath:sql/ddl.sql" /> <jdbc:script location="classpath:sql/dml.sql" /> </jdbc:initialize-database>
上面的dataSource Bean的driverClassName是org.h2.Driver, 而url是h2的内存数据库的url连接。 在jdbc:initialize-database bean中定义了在数据库的初始阶段要执行两个脚本sql/ddl.sql和dml.sql两个脚本。
经过这些配置之后,你就可以运行spring+mybatis的单元测试了,我们秀一个测试结果
4个方法全部测试通过。
我们总结下使用dbunit测试spring+mybatis应用的db操作步骤, 首先要配置pom文件引入必要依赖,然后需要在src/test/resources中添加spring配置文件,定义测试所需要的数据源和数据库初始化, 然后需要在写单元测试时在初始化sql中添加必要的数据表定义和数据初始化脚本。
最后为大家奉上整个demo项目, 文中未尽事宜,请通过demo项目了解,如果还有不解之处,请留言探讨。
hello-j.zip