`

单元测试系列之5:使用unitils测试Service层

阅读更多
引述:Spring 的测试框架为我们提供一个强大的测试环境,解决日常单元测试中遇到的大部分测试难题:如运行多个测试用例和测试方法时,Spring上下文只需创建一次;数据库现场不受破坏;方便手工指定Spring配置文件、手工设定Spring容器是否需要重新加载等。但也存在不足的地方,基本上所有的Java应用都涉及数据库,带数据库应用系统的测试难点在于数据库测试数据的准备、维护、验证及清理。Spring 测试框架并不能很好地解决所有问题。要解决这些问题,必须整合多方资源,如DbUnit、Unitils、Mokito等。其中Unitils正是这样的一个测试框架。

使用unitils测试Service层

   在进行服务层的测试之前,我们先来认识一下需要测试的UserServiceImpl服务类。UserServiceImpl服务类中拥有一个处理用户登录的服务方法,其代码如下所示。
   UserService.java
package com.baobaotao.service;
import com.baobaotao.domain.LoginLog;
import com.baobaotao.domain.User;
import com.baobaotao.dao.UserDao;
import com.baobaotao.dao.LoginLogDao;
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
	private UserDao userDao;
@Autowired
	private LoginLogDao loginLogDao;
	public void loginSuccess(User user) {
		user.setCredits( 5 + user.getCredits());
		LoginLog loginLog = new LoginLog();
		loginLog.setUserId(user.getUserId());
		loginLog.setIp(user.getLastIp());
		loginLog.setLoginTime(user.getLastVisit());
         userDao.updateLoginInfo(user);
         loginLogDao.insertLoginLog(loginLog);
	}	
   …
}

   UserServiceImpl需要调用DAO层的UserDao和LoginLogDao以及User和LoginLog这两个PO完成业务逻辑,User和LoginLog分别对应t_user和t_login_log这两张数据库表。
   在用户登录成功后调用UserServiceImpl中的loginSuccess()方法执行用户登录成功后的业务逻辑。

    [1]  登录用户添加5个积分(t_user.credits)。
    [2]  将登录用户的最后访问时间(t_user.last_visit)和IP(t_user.last_ip)更新为当前值。
    [3]  在日志表(t_login_log)中为用户添加一条登录日志。

这是一个需要访问数据库并存在数据更改操作的业务方法,它工作在事务环境下。下面是装配该服务类Bean的Spring配置文件。
  baobaotao-service.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
	<context:component-scan base-package="com.baobaotao.service"/>
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource" />
	<tx:annotation-driven />
 <!-- 使用aop/tx命名空间配置事务管理,这里对service包下的服务类方法提供事务-->
     <aop:config>
		<aop:pointcut id="jdbcServiceMethod"
			expression= "within(com.baobaotao.service..*)" />
		<aop:advisor pointcut-ref="jdbcServiceMethod" advice-ref="jdbcTxAdvice" />
     </aop:config>
     <tx:advice id="jdbcTxAdvice" transaction-manager="transactionManager">
		<tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
	</tx:advice>
</beans>

   UserServiceImpl所关联的DAO类和PO类都比较简单,这里就不一一列出,读者可以参考本文附带光盘中的实例代码。在着手测试UserServiceImpl之前,需要先创建数据库表,相应的SQL脚本文件位于:D:\masterSpring\Chapter 16\schema目录下。
   下面我们为UserServiceImpl编写一个简单的测试用例类,此时的目标是让这个基于Unitils测试框架的测试类运行起来,并联合Mockito框架创建Dao模拟对象。首先编写测试UserService#findUserByUserName()方法的测试用例,如代码清单16-37所示:
   UserServiceTest.java
package com.baobaotao.service;

import org.unitils.UnitilsJUnit4;
import org.unitils.spring.annotation.SpringApplicationContext;
import org.springframework.test.util.ReflectionTestUtils;
import org.unitils.spring.annotation.SpringBean;
import org.junit.Test;
import com.baobaotao.domain.User;
import java.util.Date;
…
@SpringApplicationContext({"baobaotao-service.xml", "baobaotao-dao.xml"}) //①加载Spring配置文件
public class UserServiceTest extends UnitilsJUnit4{

private UserDao userDao; //② 声明用户Dao
private LoginLogDao loginLogDao;
	
@Before //③ 创建Dao模拟对象
public void init(){
	userDao = mock(UserDao.class);
	loginLogDao = mock(LoginLogDao.class);
}

	@Test  //④ 设置成为JUnit测试方法
	public void findUserByUserName() {

//④-1 模拟测试数据
		 User user = new User();
		 user.setUserName("tom");
		 user.setPassword("1234");
		 user.setCredits(100);
		 doReturn(user).when(userDao).findUserByUserName("tom"); 

//④-2 实例化用户服务实例类
		 UserServiceImpl userService = new UserServiceImpl();

//④-3通过Spring测试框架提供的工具类为目标对象私有属性设值
		 ReflectionTestUtils.setField(userService, "userDao", userDao);
		
//④-4 验证服务方法
		 User u = userService.findUserByUserName("tom");
		 assertNotNull(u);
		 assertThat(u.getUserName(),equalTo(user.getUserName()));

//④-5 验证交互行为
	      verify(userDao,times(1)).findUserByUserName("tom");
	}

}

   这里,我们让UserServiceTest直接继承于Unitils所提供的UnitilsJUnit4的抽象测试类,该抽象测试类的作用是让Unitils测试框架可以在JUnit 测试框架基础上运行起来。在①处,标注了一个类级的@SpringApplicationContext注解,这里Unitils将从类路径中加载Spring配置文件,并使用该配置文件启动Spring容器。在③处通过Mockito创建两个模拟DAO实例。在④-1处模拟测试数据并通过Mockito录制UserDao#findUserByUserName()行为。在④-2处实例化用户服务实例类,并在④-3处通过Spring测试框架提供的工具类org.springframework.test.util.ReflectionTestUtils为userService私有属性userDao赋值(ReflectionTestUtils是一个访问测试对象中私有属性非常好用的工具类)。在④-4处调用服务UserService#findUserByUserName()方法,并验证返回结果。在④-5处通过Mockito验证模拟userDao对象是否被调用,且只调用一次。最后在IDE中执行UserServiceTest测试用例,测试结果如图16-15所示。
   从运行结果可以看出,我们已成功对UserServceTest.findUserByUserName()执行单元测试。下面我们通过Unitils提供的@DataSet注解来准备测试数据,并测试UserService# loginSuccess ()方法。BaobaoTao.SaveUsers.xls数据集如图16-16所示。

   准备好了测试数据集之后,就可以开始为UserServiceImpl编写测试用例类,此时的目标是通过Unitils提供的@DataSet注解准备测试数据,来保证测试数据的独立性,避免手工通过事务回滚维护测试数据的状态。测试UserService#loginSuccess ()方法的代码如下所示。
   代码清单16 38  UserServiceTest.java
package com.baobaotao.service;
import org.unitils.UnitilsJUnit4;
import org.unitils.spring.annotation.SpringApplicationContext;
import org.unitils.spring.annotation.SpringBean;
import org.junit.Test;
import com.baobaotao.domain.User;
import java.util.Date;
…
@SpringApplicationContext({"baobaotao-service.xml", "baobaotao-dao.xml"}) //①加载Spring配置文件
public class UserServiceTest extends UnitilsJUnit4{

//② 从Spring容器中加载UserService实例
	@SpringBean("userService")
	private UserService userService;

@Test
	@DataSet("BaobaoTao.SaveUsers.xls")//③ 准备验证数据
	public void loginSuccess() {
		User user = userService.findUserByUserName("tom"); //④-1 加载"tom"用户信息
		Date now = new Date();
		user.setLastVisit(now); //④-2 设置当前登录时间
	     userService.loginSuccess(user); //④-3 user登录成功,更新其积分及添加日志
	     User u = userService.findUserByUserName("tom");
	     assertThat(u.getCredits(),is(105)); //⑤ 验证登录成功之后,用户积分
	}	
}

   在①处通过加载Unitils的@SpringApplicationContext 注解加载Spring配置文件,并初始化Spring容器。在②处通过@ SpringBean注解从Spring容器中获取UserService实例。在③处通过@DataSet注解从当前测试用例所在类路径中加载BaobaoTao.SaveUsers.xls数据集,并将数据集中的数据保存到测试数据库相应的表中。从上面的数据集中可以看出,我们为t_user表准备了两条用户信息测试数据。在④-1处从测试数据库中获取“tom”用户信息,模拟当前登录的用户。在④-2处设置当前“tom”用户的登录时间。在④-3处调用UserService#loginSuccess()方法,更新“tom”用户积分,并持久化到测试数据库中。在⑤处,验证“tom”用户当前积分是否是105分。完成测试用例的编写,最后在IDE中执行UserServiceTest测试用例,测试结果如图16-17所示。

   从运行结果可以看出,我们已成功对UserServce#loginSuccess()执行单元测试。重复执行当前单元测试,测试结果仍然通过。细心的读者可能会有疑问,没有UserServce# loginSuccess()测试方法实施事务回滚,执行多次之后“tom”用户的积分不应该是105分,那为何测试还是通过呢?这是因为Unitils帮我们维护测试数据库中的数据状态,Unitils这个强大的魔力,归根于Unitils强大的数据集更新策略。到此我们成功完成UserServce单元测试。从上面为用户服务UserServce编写两个测试方法可以看出,对service层的测试,我们既可以采用JUnit+Unitils+Mockito组合,运用Mockito强大的模块能力,完成service层独立性测试,也可以采用JUnit+Unitils+Dbunit组合,运用Dbunit强大的数据库维护能力,完成service层+DAO层集成测试。

引用
提示:在实际项目中,我们只对DAO做集成测试,在对Service层测试中,对所有的DAO都用Mockito模拟,也即只对Service层单元测试。


  这些文章摘自于我的《Spring 4.x企业应用开发实战》的第16章,我将通过连载的方式,陆续在此发出。欢迎大家讨论。
  • 大小: 97.9 KB
  • 大小: 37 KB
分享到:
评论

相关推荐

    使用Unitils测试DAO

    综上所述,"使用Unitils测试DAO"的主题涵盖了如何利用Unitils库和DBUnit组件来创建可靠的DAO层测试,包括数据库的初始化、数据集的管理、断言的使用,以及自定义工具类的创建和源码分析。这种测试方法有助于提升代码...

    Unitils单元测试

    Unitils 是一个强大的Java测试框架,它旨在简化单元测试的过程并提高测试的可维护性。该框架基于DbUnit和EasyMock,并与JUnit和TestNG兼容,提供了对数据库测试、Mock对象管理和Spring集成的支持。 在数据库测试...

    junit单元测试jar包集

    这里提到的四个文件是Java开发中常用的单元测试框架和库,分别是JUnit、DBUnit、Unitils和Mockito。让我们逐一深入探讨它们的功能和使用方法。 **JUnit** 是Java领域中最广泛使用的单元测试框架,这里的`junit-4.11...

    Unitils示例

    5. **使用 Unitils 进行测试** 在使用 Unitils 进行测试时,我们可以利用其提供的注解,例如 `@UsingDataSet` 来加载数据库数据,`@TestedObject` 或 `@InjectFields` 来注入测试对象。同时,配合 EasyMock,我们...

    Unitils教程(介绍Unitils的最佳资料)

    本教程将向您展示如何使用 Unitils 来编写单元测试,并介绍一些常见的单元测试技术。 断言应用 ---------- 在单元测试中,断言是非常重要的一部分。断言的目的是为了验证测试结果是否与预期结果相符。Unitils 提供...

    unitils整合dbunit利用excel进行单元测试

    unitils整合dbunit利用excel进行单元测试 包含mock以及整合spring进行测试

    Integrete unitils for database(dao) testing

    5. **DAO支持**:Unitils还提供了对各种ORM框架(如Hibernate、JPA)的集成,简化了DAO层的测试。它可以自动管理实体的持久化状态,无需手动调用flush和clear方法。 6. **代码覆盖率**:虽然Unitils本身并不直接...

    unitils-3.3-with-dependencies.zip

    10. **持久层测试**:支持对实体和会话工厂的验证,确保数据操作的正确性。 这个压缩包中的"unitils-3.3"可能包含了以下文件结构: - `unitils-3.3.jar`:主库文件,包含Unitils的核心功能。 - `unitils-dbunit....

    Unitils-jar包

    Unitils 测试框架目的是让单元测试变得更加容易和可维护。Unitils 构建在DbUnit 与 EasyMock 项目之上并与JUnit 和TestNG 相结合。支持数据库测试,支持利用Mock 对象 进行测试并提供与Spring 和Hibernate 相集成。...

    unitils-2.2

    Unitils 是一个强大的Java开发框架,专为简化单元测试和集成测试而设计。它提供了对数据库、Spring框架以及其他模块的全面支持,使得测试过程更加高效和便捷。在深入探讨Unitils的功能之前,让我们先理解一下单元...

    对dbunit进行mybatis DAO层Excel单元测试(必看篇)

    一、Mybatis DAO层测试难点 在mybatis DAO层中,对数据库的单元测试存在一些难点: 1. 可重复性:每次运行单元测试,得到的数据是重复的独立的。 2. 独立性:测试数据与实际数据相互独立。 3. 数据库中脏数据...

    接口测试白皮书--淘宝(中国)软件有限公司

    Unitils 是一个面向 Java 的测试工具集合,它提供了一系列增强单元测试的工具类。对于复杂的接口测试场景,Unitils 可以简化对象初始化、数据库操作等任务。 ##### 5.5 TestNG TestNG 是一个用于 Java 的测试框架,...

    spock-unitils-0.7-groovy-2.0.zip

    Unitils则是一个专注于简化单元测试的Java库,它提供了诸如数据库和持久层支持等功能。Groovy是Java平台上的动态语言,与Java代码可互操作,其语法简洁,易于编写。 描述中的 "bytesize.zip" 提到了一个用于处理...

    改资源配准测试

    总之,“改资源配准测试”是一项关键的IT实践,它涵盖了单元测试、数据库管理和自动化工具的使用。通过熟练掌握Unitils和DBUtils,开发者可以更有效地管理和测试项目中的资源,确保系统的稳定性和可靠性。而`chapter...

    unitils-selenium:使用 Selenium 测试 Web 应用程序的 Unitils 模块-开源

    Unitils 模块 - 启动 Selenium Web 驱动程序以测试 Web 应用程序的简单方法。

    Spring.3.x企业应用开发实战(完整版).part2

    16.6 使用unitils测试Service层 16.7 测试Web层 16.7.1 对LoginController进行单元测试 16.7.2 使用Spring Servlet API模拟对象 16.7.3 使用Spring RestTemplate测试 16.7.4 使用Selenium测试 16.8 小结 第17章 实战...

    Spring3.x企业应用开发实战(完整版) part1

    16.6 使用unitils测试Service层 16.7 测试Web层 16.7.1 对LoginController进行单元测试 16.7.2 使用Spring Servlet API模拟对象 16.7.3 使用Spring RestTemplate测试 16.7.4 使用Selenium测试 16.8 小结 第17章 实战...

Global site tag (gtag.js) - Google Analytics