最近一個(gè)項(xiàng)目使用的是struts2+Spring3+mybatis3的技術(shù)框架,由于開(kāi)發(fā)人員都不熟悉如何進(jìn)行單元測(cè)試,今天有空,簡(jiǎn)單研究了一下如何用junit4來(lái)測(cè)試基于這個(gè)框架的代碼。由于struts的action只是負(fù)責(zé)前臺(tái)的請(qǐng)求轉(zhuǎn)發(fā),而所有的業(yè)務(wù)都是在service層處理,因此一般情況下只需對(duì)service進(jìn)行單元測(cè)試,而不需要對(duì)action進(jìn)行單元測(cè)試。下面介紹一個(gè)簡(jiǎn)單的例子:

開(kāi)發(fā)環(huán)境:

SystemWindows xp
IDEeclipse Java EE 3.6
DatabaseMySQL

開(kāi)發(fā)依賴(lài)庫(kù):

JavaEE5Spring 3.0.5Mybatis 3.0.4myBatis-spring-1.0junit4.8.1

一、準(zhǔn)備工作:

1、下載jar
Spring3 jar下載:

http://ebr.springsource.com/repository/app/library/version/detail?name=org.springframework.spring&version=3.0.5.RELEASE

MyBatis3 jar 下載:
http://www.mybatis.org/java.html
junit 4 jar下載:
http://www.junit.org/

 

2、 添加的jar包如下:


3、創(chuàng)建mysql的數(shù)據(jù)庫(kù)表,步驟如下:
1、進(jìn)入mysql的安裝路徑,假設(shè)在:C:\Program Files\MySQL\MySQL Server 5.1\bin;
2、輸入命令:mysql -uroot -p,enter,輸入密碼:admin;
3、mysql>use test;
5、mysql>grant all privileges on test.* to test@'localhost' identified by 'test';
6、mysql>flush privileges;

4、mysql>
  create table account_bak(account_id int not null auto_increment,
                     username varchar(20),
                     password varchar(20),
                     create_time datetime,
                     primary key(account_id));

二、spring 和mybatis整合
1、在eclipse中創(chuàng)建一個(gè)java project,目錄結(jié)構(gòu)如下:


這是一個(gè)標(biāo)準(zhǔn)的maven工程的目錄結(jié)構(gòu),下面逐一介紹上圖涉及到的文件。
2、創(chuàng)建mybatis的配置文件mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

   

</configuration>

上面的配置文件中,可以加入一些公共、常用的MyBatis方面的全局配置。handlerobjectFactoryplugin、以及mappers的映射路徑(由于在spring配置文件spring.xml中的SqlSessionFactoryBean有配置mapperlocation,這里就不需要配置)等。這個(gè)文件名稱(chēng)和下面的spring.xml中的configLocation中的值對(duì)應(yīng),不是隨便寫(xiě)的。
3、創(chuàng)建spring的配置文件spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=" xmlns:xsi=" xmlns:jee="   http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-2.5.xsd
   http://www.springframework.org/schema/jee
   http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
   http://www.springframework.org/schema/tx
   

 <bean
  class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
 
 <bean id="dataSource"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=UTF-8" />
  <property name="username" value="test" />
  <property name="password" value="test" />
 </bean>
 
 <!-- 配置事務(wù)管理器,注意這里的dataSource和SqlSessionFactoryBean的dataSource要一致,不然事務(wù)就沒(méi)有作用了 -->
 <bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
 </bean>

 <tx:annotation-driven transaction-manager="transactionManager" />
 <!-- myBatis文件 -->

 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="configLocation" value="classpath:mybatis.xml" />
  <property name="mapperLocations"
   value="classpath*:com/glen/model/*.xml" />
  <property name="dataSource" ref="dataSource" />
 </bean>
 
 <bean id="accountDao" class="com.glen.dao.AccountDao">
  <property name="sessionFactory" ref="sqlSessionFactory"/>
 </bean>
 
 <bean id="accountService" class="com.glen.service.AccountService">
  <property name="accountDao" ref="accountDao"/>
 </bean>
 
 <context:annotation-config />
 <context:component-scan base-package="com.glen" />

</beans>

4、JavaBeanModelEntity)相關(guān)類(lèi)、及mybatis 的mapper對(duì)象

javabean:

package com.glen.model;

import java.io.Serializable;
import java.util.Date;

public class Account implements Serializable {

 private static final long serialVersionUID = -7970848646314840509L;
 
 private Integer accountId;
 private String username;
 private String password;
 private Date createTime;

 public Account() {
  super();
 }
//下面是getter、setters

account-resultMap.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="accountMap">

    <resultMap type="com.hoo.entity.Account" id="accountResultMap">

       <id property="accountId" column="account_id"/>

       <result property="username" column="username"/>

       <result property="password" column="password"/>

       <result property="createTime" column="create_time"/>

    </resultMap>

</mapper>

 

account-mapper.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "

<mapper namespace="account">

 
 <select id="getList" parameterType="com.glen.model.Account" resultType="list" resultMap="accountResultMap">
   select * from account where username like '%' #{username} '%'
 </select>
 
 <select id="getAllAccount" resultType="list" resultMap="accountResultMap">
   select * from account
 </select>
 
 
 <!-- accountResultMap是account-resultmap.xml中定義的resultmap -->
 <select id="get" parameterType="com.glen.model.Account" resultType="com.glen.model.Account" resultMap="accountResultMap">
  <![CDATA[
   select * from account where account_id = #{accountId}
        ]]>
 </select>
 
 
 <!-- 自動(dòng)生成id策略 -->
 <insert id="add" useGeneratedKeys="true" keyProperty="accountId" parameterType="com.glen.model.Account">
  insert into account(account_id, username, password)
  values(#{accountId,jdbcType=BIGINT}, #{username}, #{password})
<!--將最后插入的逐漸返回到j(luò)ava對(duì)象-->
  <selectKey resultType="int" keyProperty="accountId">
   SELECT LAST_INSERT_ID()
  </selectKey>
  
 </insert>
 
 <update id="edit" parameterType="com.glen.model.Account">
  update account set
  username = #{username},
  password = #{password}
  where account_id = #{accountId}
 </update>
 
 <delete id="remove" parameterType="com.glen.model.Account">
  delete from account where account_id = #{accountId}
 </delete>
  
</mapper> 


5、創(chuàng)建dao:

 

package com.glen.dao;

import javax.annotation.Resource;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Repository;

import com.glen.model.Account;


public class AccountDao {

 private SqlSessionFactory sessionFactory;

 public AccountDao() {

 }

 public SqlSessionFactory getSessionFactory() {
  return sessionFactory;
 }

 public void setSessionFactory(SqlSessionFactory sessionFactory) {
  this.sessionFactory = sessionFactory;
 }

 public void insert(Account account) {

  SqlSession session = sessionFactory.openSession();
  session.insert("account.add", account);
 }
 
 public Account getAccountById(Account account) {
  
  SqlSession session = sessionFactory.openSession();
  Account accountFromDb = (Account)session.selectOne("account.get", account);
  return accountFromDb;
 }
}

 

6、創(chuàng)建service:

 

package com.glen.service;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.glen.dao.AccountDao;
import com.glen.model.Account;


public class AccountService {
 
 private AccountDao accountDao;
 
 /**
  * 新增一個(gè)帳戶(hù)。
  * @param account
  */
 public void insertAccount(Account account) {
  
  accountDao.insert(account);
 }

 /**
  * 根據(jù)帳戶(hù)ID查找?guī)?hù)信息
  * @param account
  * @return
  */
 public Account getAccountById(Account account) {
  
  return accountDao.getAccountById(account);
 }
 
 public AccountDao getAccountDao() {
  return accountDao;
 }

 public void setAccountDao(AccountDao accountDao) {
  this.accountDao = accountDao;
 }
}

 


Ok,至此spring 和mybatis就整合好了。

三、用junit進(jìn)行單元測(cè)試
在src/test/java目錄下,創(chuàng)建一個(gè)測(cè)試類(lèi):TestAccountService:

 

package com.glen.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.glen.model.Account;


public class TestAccountService {
 
 Logger logger = Logger.getLogger("TestAccountService");
 
 AccountService service = null;

 @Before
 public void init() {
  
  ApplicationContext aCtx = new FileSystemXmlApplicationContext(
    "classpath:spring.xml");
  AccountService service = (AccountService) aCtx
    .getBean("accountService");
  assertNotNull(service);
  this.service = service;

 }
 
 @Test
 public void testInsertAccount() {

  // 創(chuàng)建一個(gè)帳戶(hù)
  Account account = new Account();
  // account.setAccountId(1);
  account.setUsername("selina");
  account.setPassword("123456");
  
  //將創(chuàng)建的帳戶(hù)插入到數(shù)據(jù)庫(kù)中
  service.insertAccount(account);
  logger.debug("account id: " + account.getAccountId());
  
  //從數(shù)據(jù)庫(kù)獲取剛才插入的帳戶(hù)
  Account accountFromDb = service.getAccountById(account);
  assertNotNull(accountFromDb);
  assertEquals(account.getAccountId(), accountFromDb.getAccountId());
 }

}


測(cè)試通過(guò),顯示如下界面:


四、使用spring的標(biāo)記來(lái)注入對(duì)象
如上所述,我們?cè)趕pring的配置文件spring.xml中,定義了兩個(gè)業(yè)務(wù)模塊相關(guān)的bean,accountDao和accountService,但是在實(shí)際項(xiàng)目中,這樣的dao和service會(huì)非常多,如果每個(gè)都要這樣定義,會(huì)造成配置文件的體積過(guò)大,可閱讀性和可維護(hù)性都會(huì)變差。

那么如何對(duì)spring.xml進(jìn)行瘦身呢?有兩種方案,第一種方案是分模塊開(kāi)發(fā),對(duì)于模塊內(nèi)部的bean,寫(xiě)在對(duì)應(yīng)模塊內(nèi)部的spring配置文件中,如:spring-account.xml;第二種方案,就是使用spring的標(biāo)記。下面我想說(shuō)說(shuō)的就是,用spring的標(biāo)記:@Service @Repository @Resource來(lái)實(shí)現(xiàn)對(duì)象的注入。在上面這個(gè)例子基礎(chǔ)上,做以下步驟的修改:

1、注釋掉spring.xml中的兩個(gè)bean:accountDao和accountService的定義

  <!-- 
 <bean id="accountDao" class="com.glen.dao.AccountDao">
  <property name="sessionFactory" ref="sqlSessionFactory"/>
 </bean>
 
 <bean id="accountService" class="com.glen.service.AccountService">
  <property name="accountDao" ref="accountDao"/>
 </bean>
 -->


2、在AccountDao類(lèi)中添加兩個(gè)標(biāo)記:@Repository和 @Resource,

 

@Repository
public class AccountDao {

 @Resource
 private SqlSessionFactory sessionFactory; 

這里添加@Repository標(biāo)記,相當(dāng)于在spring.xml中定義了一個(gè)bean,bean的id為這個(gè)類(lèi)對(duì)象名稱(chēng)的第一個(gè)字母改成小寫(xiě)后的字符串,即:accountDao。添加 @Resource標(biāo)記,相當(dāng)于在accountDao這個(gè)bean中,引用了一個(gè)“sqlSessionFactory”。

3、在AccountService類(lèi)中添加兩個(gè)標(biāo)記:@Service 和 @Resource:

@Service
public class AccountService {
 
 @Resource
 private AccountDao accountDao;


4、運(yùn)行TestAccountService,同樣測(cè)試通過(guò)。