您的位置:首页 > 编程语言 > Java开发

Spring 使用Junit 进行单元测试

2013-07-15 15:59 477 查看

1.概述

Spring 提供了单元测试的强大支持,主要特性包括:

支持主流的测试框架 Junit 和 TestNG

支持在测试类中使用依赖注入 Denpendency Injection

支持测试类的自动化事务管理

支持使用各种注释标签,提高开发效率和代码简洁性

Spring 3.1 更是支持在测试类中使用非 XML 配置方法和基于 Profile 的 bean 配置模式
下面提供一个Spring基于junit的测试用例,数据操作使用Spring data jpa ,因为以前刚好配置过,就拿过来用。

2.具体实现

实体类:User ,简单的Java bean ,就不贴了

dao:
public interface UserDao extends CrudRepository<User, Long>{

Page<User> findAll(Pageable pageable);

User findById(long id);

User findByName(String name);

User findByLoginName(String loginName);
}


解释:使用data jpa 提供的借口CrudRepository,此接口提供基本的增删改查操作。

Service类

@Service
public class AccountService implements IAccoutService{

@Autowired
private UserDao userDao;

@Transactional
public void saveUser(User user){
userDao.save(user);
}

public List<User> findUserByPage(int page){
Pageable pageable = new PageRequest(page, 10);
Page<User> page1 = userDao.findAll(pageable);

return page1.getContent() ;
}

public User findUserById(long id){
return userDao.findById(id);
}

public User findUserByLoginName(String name){
return userDao.findByLoginName(name);
}

Logger logger = LoggerFactory.getLogger(AccountService.class);

}

配置文件:

<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd"
default-lazy-init="true">

<description>Spring公共配置</description>

<!-- 读配置数据 -->
<context:property-placeholder
ignore-resource-not-found="true" location="classpath*:/application.properties" />

<!-- 注解支持 -->
<context:annotation-config />

<!-- 使用annotation 自动注册bean,并检查@Required,@Autowired的属性已被注入 -->
<context:component-scan base-package="com.blueinfo.jee">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>

<!-- Jpa Entity Manager 配置 -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
<property name="packagesToScan" value="com.blueinfo.jee.entity" />
<property name="jpaProperties">
<props>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory
</prop>
<prop key="net.sf.ehcache.configurationResourceName">cache/ehcache-hibernate-local.xml</prop>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>

<bean id="hibernateJpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="POSTGRESQL" />
</bean>

<!-- 数据源配置,使用应用内的DBCP数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<!-- Connection Info -->
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="defaultAutoCommit" value="false" />
</bean>

<!-- Spring Data Jpa配置, 扫描base-package下所有继承于Repository<T,ID>的接口 -->
<jpa:repositories base-package="com.blueinfo.jee.reposity.jpa"
transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory" />

<!-- 事务管理器配置, Jpa单数据源事务 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />

</beans>


注释:一些基本配置,主要就是使用data jpa 进行数据库操作,Service使用注解扫描。数据库参数在application.properties中设置

最后,Test类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/applicationContext.xml"})
@Transactional
public class AccountServiceTest {

@Autowired
private AccountService service;

@Test
public void testSaveUser() {
User user = new User();
user.setEmail("111@qq.com");
user.setName("李坏1232");
user.setPassword("123456");

service.saveUser(user);
}

}


注释:
1,@RunWith指定此测试类的运行者,这里用了 SpringJUnit4ClassRunner,这个类是一个针对 Junit 运行环境的自定义扩展,用来标准化在 Spring 环境中 Junit4.5 的测试用例,例如支持的注释标签的标准化
2,@ContextConfiguration 指定 Spring 配置信息的来源,支持指定 XML 文件位置或者 Spring 配置类名,这里我们指定 classpath 下的 /applocationContext.xml为配置文件的位置
3,@Transactional 注释标签是表明此测试类的事务启用,这样所有的测试方案都会自动的
rollback,即您不用自己清除自己所做的任何对数据库的变更了,测试数据不会影响原本数据。
4,Autowired 自动注入Servcie

不需要再手工加载配置文件,来获得bean。代码也更简洁。

3.更多常用测试标签

1,@Configration

使用java类来定义bean,和ContextConfiguration不同,后者使用xml来定义bean。

@Configuration
public class SpringConfig {
@Autowired
private DriverManagerDataSource datasource;//自动注入datasource

@Bean
public UserDao accountDao() {
UserDao DAO = new UserDao();
DAO.setDataSource(datasource);
return DAO;
}

@Bean
public AccountService accountService() {
return new AccountService();
}
}


通过SpringConfig定义了UserDao和AccountService
然后在配置文件applicationContent.xml中 引入
<bean class="config.SpringConfig"/>
就可以了。

2,@DirtiesContext

缺省情况下,Spring 测试框架一旦加载 applicationContext 后,将一直缓存,不会改变,但是,

由于 Spring 允许在运行期修改 applicationContext 的定义,例如在运行期获取 applicationContext,然后调用 registerSingleton 方法来动态的注册新的 bean,这样的情况下,如果我们还使用 Spring 测试框架的被修改过 applicationContext,则会带来测试问题,我们必须能够在运行期重新加载 applicationContext,这个时候,我们可以在测试类或者方法上注释:@DirtiesContext,作用如下:

如果定义在类上(缺省),则在此测试类运行完成后,重新加载 applicationContext

如果定义在方法上,即表示测试方法运行完成后,重新加载 applicationContext

3,@TransactionConfigration 和 @Rollback

缺省情况下,Spring 测试框架将事务管理委托到名为 transactionManager 的
bean 上,如果您的事务管理器不是这个名字,那需要指定 transactionManager 属性名称,还可以指定 defaultRollback 属性,缺省为 true,即所有的方法都 rollback,您可以指定为 false,这样,在一些需要 rollback 的方法,指定注释标签 @Rollback(true)即可。

4.对junit4标签的支持

了上面 Spring 测试框架的注释标签,我们来看看一些常见的基于 Junit4 的注释标签在 Spring 测试环境中的使用方法。

1,Test(Expected=...)

此注释标签的含义是,这是一个测试,期待一个异常的发生,期待的异常通过 xxx.class 标识。例如,我们修改 AccountService.Java 的 insertIfNotExist 方法,对于传入的参数如果为空,则抛出 IllegalArgumentException,如下:

public void insertIfNotExist(Account account) {
if(account==null)
throw new IllegalArgumentException("account is null");
Account acct = accountDao.getAccountById(account.getId());
if(acct==null) {
log.debug("No "+account+" found,would insert it.");
accountDao.saveAccount(account);
}
acct = null;
}

然后,在测试类中增加一个测试异常的方法,如下:
@Test(expected=IllegalArgumentException.class)
public void testInsertException() {
service.insertIfNotExist(null);
}

运行结果是 green bar。

2,@Test(timeout=3000)

可以给测试方法指定超时时间(毫秒级别),当测试方法的执行时间超过此值,则失败。

比如在 AccountService 中增加如下方法:
public void doSomeHugeJob() {
try {
Thread.sleep(2*1000);
} catch (InterruptedException e) {
}
}

上述方法模拟任务执行时间 2 秒,则测试方法如下:
@Test(timeout=3000)
public void testHugeJob() {
service.doSomeHugeJob();
}

上述测试方法期待 service.doSomeHugeJob 方法能在 3 秒内结束,执行测试结果是 green bar。

3,@Repeat

通过 @Repeat,您可以轻松的多次执行测试用例,而不用自己写 for 循环,使用方法:
@Repeat(3)
@Test(expected=IllegalArgumentException.class)
public void testInsertException() {
service.insertIfNotExist(null);
}

这样,testInsertException 就能被执行 3 次。

4,@ActiveProfiles

从 Spring 3.2 以后,Spring 开始支持使用 @ActiveProfiles 来指定测试类加载的配置包,比如您的配置文件只有一个,但是需要兼容生产环境的配置和单元测试的配置,那么您可以使用 profile 的方式来定义 beans,如下:

<beans xmlns="http://www.Springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.Springframework.org/schema/beans http://www.Springframework.org/schema/beans/Spring-beans-3.2.xsd"> <beans profile="test">
<bean id="datasource"
class="org.Springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:hsql://localhost" />
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
</beans>

<beans profile="production">
<bean id="datasource"
class="org.Springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:hsql://localhost/prod" />
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
</beans>
<beans profile="test,production">
<bean id="transactionManager"
class="org.Springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<bean id="initer" init-method="init" class="service.Initializer">
</bean>
<bean id="accountDao" depends-on="initer" class="DAO.AccountDao">
<property name="dataSource" ref="datasource"/>
</bean>

<bean id="accountService" class="service.AccountService">
</bean>
</beans>
</beans>

上面的定义,我们看到:

在 XML 头中我们引用了 Spring 3.2 的 beans 定义,因为只有 Spring 3.2+ 才支持基于 profile 的定义

在 <beans> 根节点下可以嵌套 <beans> 定义,要指定 profile 属性,这个配置中,我们定义了两个 datasource,一个属于 test profile,一个输入 production profile,这样,我们就能在测试程序中加载 test profile,不影响 production 数据库了

在下面定义了一些属于两个 profile 的 beans,即 <beans profile=”test,production”> 这样方便重用一些 bean 的定义,因为这些 bean 在两个 profile 中都是一样的

使用方法

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContent.xml")
@Transactional
@ActiveProfiles("test")
public class AccountServiceTest {
...
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: