干货:Dao单元测试项目实战

Dao单元测试技术方案:H2+spring-test+spring-test-dbunit+testng

一. 技术方案的选择

H2:内存数据库,支持标准SQL,相当于把数据库本地化,可以避免对测试环境的依赖,也可以提升单测的速度。

spring-test: 提供了@DirtiesContex AbstractTestNGSpringContextTests等诸多注释和基类,可以用来简化单元测试。

spring-test-dbunit:提供了对dbunit的注解封装,可以用来提供测试数据和进行数据验证。

testng: 提供了相比junit更丰富的测试功能,使用起来更方便。

二.项目实战

  1. 新建一个名为DaoTestExample的maven工程,结构如下图:

1111.png

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>


四、总结
此技术方案可以实现单元测试与测试环境的隔离,自动化测试、数据隔离等在[重新认识单元测试]一篇中提到的单元测试规范。

20070706212834872.jpg

转载于:https://www.cnblogs.com/jishujinjie/p/7294896.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DAO编写单元测试,其中的如下<br>package com.javaeedev.dao.impl;<br><br>import java.util.List;<br>import java.util.UUID;<br><br>import com.javaeedev.dao.UserDao;<br>import com.javaeedev.domain.PasswordTicket;<br>import com.javaeedev.domain.User;<br>import com.javaeedev.exception.LockException;<br>import com.javaeedev.util.HibernateUtil;<br><br>public class UserDaoImpl implements UserDao {<br><br> public User queryForSignOn(String username) {<br> User user = queryUser(username);<br> if(user.getLocked())<br> throw new LockException(user.getLockDate());<br> return user;<br> }<br><br> public User queryUser(String username) {<br> return (User) HibernateUtil.query(User.class, username);<br> }<br><br> public void createUser(User user) {<br> user.setEmailValidation((int)(Math.random() * 1000000) + 0xf);<br> HibernateUtil.createEntity(user);<br> }<br><br> public void updateUser(User user) {<br> HibernateUtil.updateEntity(user);<br> }<br><br> public boolean updateEmailValidation(String username, int ticket) {<br> if(ticket==0)<br> return false;<br> return 1==HibernateUtil.executeUpdate(<br> "update User as u set u.emailValidation=0 where u.username=? and u.emailValidation=?",<br> new Object[] { username, ticket }<br> );<br> }<br><br> public String createPasswordTicket(User user) {<br> HibernateUtil.executeUpdate(<br> "delete from PasswordTicket as pt where pt.user=?",<br> new Object[] { user }<br> );<br> String ticket = UUID.randomUUID().toString().replaceAll("\\-", "");<br> PasswordTicket pt = new PasswordTicket();<br> pt.setUser(user);<br> pt.setTicket(ticket);<br> pt.setCreatedDate(System.currentTimeMillis());<br> HibernateUtil.createEntity(pt);<br> return ticket;<br> }<br><br> public boolean updatePassword(String username, String oldPassword, String newPassword) {<br> if(!newPassword.matches(User.REGEX_PASSWORD))<br> return false;<br> return 1==HibernateUtil.executeUpdate(<br> "update User as u set u.password=? where u.username=? and u.password=?",<br> new Object[] { newPassword, username, oldPassword }<br> );<br> }<br><br> public boolean queryResetPassword(User user, String ticket) {<br> long time = System.currentTimeMillis() - 48L * 3600000L;<br> return !HibernateUtil.queryForList(<br> "select pt from PasswordTicket as pt where pt.user=? and pt.ticket=? and pt.createdDate>?",<br> new Object[] { user, ticket, time}<br> ).isEmpty();<br> }<br><br> @SuppressWarnings("unchecked")<br> public boolean updateResetPassword(User user, String ticket, String password) {<br> if(!password.matches(User.REGEX_PASSWORD))<br> return false;<br> long time = System.currentTimeMillis() - 48L * 3600000L;<br> List<PasswordTicket> list = HibernateUtil.queryForList(<br> "select pt from PasswordTicket as pt where pt.user=? and pt.ticket=? and pt.createdDate>?",<br> new Object[] { user, ticket, time}<br> );<br> if(list.isEmpty())<br> return false;<br> HibernateUtil.executeUpdate(<br> "delete from PasswordTicket as pt where pt.user=?",<br> new Object[] { user }<br> );<br> HibernateUtil.executeUpdate(<br> "update User as u set u.password=? where u.username=?",<br> new Object[] { password, user.getUsername() }<br> );<br> return true;<br> }<br><br> public void updateLock(User user, long lockTime) {<br> HibernateUtil.executeUpdate(<br> "update User as u set u.lockDate=? where u.username=?",<br> new Object[] { System.currentTimeMillis() + lockTime, user.getUsername() }<br> );<br> }<br><br> public void updateUnlock(User user) {<br> HibernateUtil.executeUpdate(<br> "update User as u set u.lockDate=0 where u.username=?",<br> new Object[] { user.getUsername() }<br> );<br> }<br><br>}<br>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值