Dao單元測試技術方案:H2+spring-test+spring-test-dbunit+testng
一. 技術方案的選擇
H2:記憶體資料庫,支援标準SQL,相當于把資料庫本地化,可以避免對測試環境的依賴,也可以提升單測的速度。
spring-test: 提供了@DirtiesContex AbstractTestNGSpringContextTests等諸多注釋和基類,可以用來簡化單元測試。
spring-test-dbunit:提供了對dbunit的注解封裝,可以用來提供測試資料和進行資料驗證。
testng: 提供了相比junit更豐富的測試功能,使用起來更友善。
二.項目實戰
- 建立一個名為DaoTestExample的maven工程,結構如下圖:

2.在test目錄下建立一個schema.sql檔案,寫入一個User表的建表語句,這個檔案是用來初始化H2資料庫,在測試用例執行前建好所需要的表結構。
CREATE TABLE `User` (
`AutoId` bigint(20) NOT NULL AUTO_INCREMENT,
`UserId` bigint(20) NOT NULL COMMENT '使用者Id',
`UserName` varchar(64) NOT NULL COMMENT '使用者姓名',
`Age` int(10) NOT NULL COMMENT '年齡',
`PointValue` int(11) NOT NULL DEFAULT '0' COMMENT '積分',
`Status` smallint(6) NOT NULL DEFAULT '0' COMMENT '記錄可用狀态',
`CreateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '記錄建立日期',
`LastModifyTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最後修改日期',
PRIMARY KEY (`AutoId`)
);
3.h2資料源配置初始化
@Configuration
@MapperScan(basePackages = "dao.mapper")
@ComponentScan({"dao"})
public class H2Config {
@Bean
public DataSource h2DataSource(){
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase database = builder.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql") //啟動時初始化建表語句
.build();
return database;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
//加載所有的sql mapper檔案
Resource[] mapperLocations = resolver.getResources("classpath:/mapper/*.xml");
sessionFactory.setMapperLocations(mapperLocations);
return sessionFactory.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
4.dao層測試基類
@SpringApplicationConfiguration(classes = {H2Config.class})
@TestExecutionListeners({ DbUnitTestExecutionListener.class })
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
@DbUnitConfiguration(databaseConnection = { "h2DataSource" })
public class DaoBaseTest extends AbstractTestNGSpringContextTests {
}
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) 會在每個測試用例執行完後清除資料庫。
@TestExecutionListeners({ DbUnitTestExecutionListener.class }) 使得dbunit的注解生效,不然spring-test-dbunit提供的注解不會啟用。
@DbUnitConfiguration(databaseConnection = { "h2DataSource" }) 指定資料源
由于項目中使用的是testng,為了 擷取dao對象,需要繼承一個spring-test中的基類AbstractTestNGSpringContextTests,利用此基類中的applicationContext來初始化、擷取注入對象。
不過僅僅繼承AbstractTestNGSpringContextTests還無法實作對象注入,我們需要引入 @MapperScan 和 @ComponentScan
所有的dao測試都要繼承DaoBaseTest
5.建立dbunit初始化資料庫和校驗資料所需的xml檔案
user-setUpData.xml 檔案,測試用例執行之前所需要準備的測試資料
User是表名,UserId="200" UserName="tony" Age="25" pointValue="2000" 是表中的部分字段(下同)
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
<User UserId="200" UserName="tony" Age="25" pointValue="2000" />
</dataset>
addUser-expectedData.xml檔案,用來校驗插入資料庫的資料是否符合預期。
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
<User UserId="200" UserName="tony" Age="25" pointValue="2000" />
</dataset>
updatePoint-exceptedData.xml 檔案,用來校驗更新積分後的資料是否符合預期
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
<User UserId="200" UserName="tony" Age="25" pointValue="2500" />
</dataset>
6.被測試的dao類
public interface UserDao {
int addUser(User user);
int updatePointValue(Long userId,Long pointValue);
User getUserInfo(Long userId);
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private UserMapper userMapper;
@Override
public int addUser(User user) {
return userMapper.addUser(user);
}
@Override
public int updatePointValue(Long userId, Long pointValue) {
return userMapper.updatePointValue(userId,pointValue);
}
@Override
public User getUserInfo(Long userId) {
return userMapper.getUserInfo(userId);
}
}
7.userMapper.xml 檔案
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="dao.mapper.UserMapper">
<resultMap id="BaseResultMap" type="dao.entity.User">
<id column="AutoId" property="id" jdbcType="BIGINT"/>
<result column="UserId" property="userId" jdbcType="BIGINT"/>
<result column="UserName" property="name" jdbcType="VARCHAR"/>
<result column="Age" property="age" jdbcType="INTEGER"/>
<result column="PointValue" property="pointValue" jdbcType="INTEGER"/>
<result column="Status" property="status" jdbcType="SMALLINT"/>
<result column="CreateTime" property="createTime" jdbcType="TIMESTAMP"/>
<result column="LastModifyTime" property="lastModifyTime" jdbcType="TIMESTAMP"/>
</resultMap>
<insert id="addUser" useGeneratedKeys="true" keyProperty="id" parameterType="dao.entity.User">
INSERT INTO User(UserId,UserName,Age,PointValue,Status,CreateTime,LastModifyTime)
VALUES(#{userId},#{name},#{age},#{pointValue},0,now(),now())
</insert>
<update id="updatePointValue">
UPDATE User SET PointValue = #{pointValue}
WHERE UserId=#{userId}
</update>
<select id="getUserInfo" resultMap = "BaseResultMap">
SELECT UserId,UserName,Age,PointValue FROM User
WHERE UserId=#{userId}
</select>
</mapper>
8.User實體類
public class User {
private Long id;
private Long userId;
private String name;
private Integer age;
private Integer pointValue;
private Integer status;
private Date createTime;
private Date lastModifyTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getPointValue() {
return pointValue;
}
public void setPointValue(Integer pointValue) {
this.pointValue = pointValue;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getLastModifyTime() {
return lastModifyTime;
}
public void setLastModifyTime(Date lastModifyTime) {
this.lastModifyTime = lastModifyTime;
}
}
9.測試用例
public class UserDaoTest extends DaoBaseTest {
@Autowired
private UserDao userDao;
@Test
@ExpectedDatabase(table = "User",
assertionMode= DatabaseAssertionMode.NON_STRICT,
value= "/data/addUser-expectedData.xml")
public void testAddUser() throws Exception {
User user = new User();
user.setUserId(200L);
user.setAge(25);
user.setPointValue(2000);
user.setName("tony");
int result = userDao.addUser(user);
Assert.assertEquals(result,1);
}
@Test
@DatabaseSetup("/data/user-setUpData.xml")
@ExpectedDatabase(table = "User",
assertionMode= DatabaseAssertionMode.NON_STRICT,
value= "/data/updatePoint-exceptedData.xml")
public void testUpdatePointValue() throws Exception {
int result = userDao.updatePointValue(200L,2500L);
Assert.assertEquals(result,1);
}
@Test
@DatabaseSetup("/data/user-setUpData.xml")
public void testGetUserInfo() throws Exception {
User user = userDao.getUserInfo(200L);
Assert.assertNotNull(user);
Assert.assertEquals(user.getAge().intValue(),25);
Assert.assertEquals(user.getName(),"tony");
Assert.assertEquals(user.getUserId().intValue(),200);
Assert.assertEquals(user.getPointValue().intValue(),2000);
}
}
三、pom依賴
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.springtestdbunit</groupId>
<artifactId>spring-test-dbunit</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.5.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.195</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
四、總結
此技術方案可以實作單元測試與測試環境的隔離,自動化測試、資料隔離等在[重新認識單元測試]一篇中提到的單元測試規範。
轉載于:https://www.cnblogs.com/jishujinjie/p/7294896.html