边城愚人

如果我不在边城,我一定是在前往边城的路上。

  语源科技BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  31 随笔 :: 0 文章 :: 96 评论 :: 0 Trackbacks

        做 java 企业级开发时,我们通常采用三层架构。特别地,如果我们要做的系统的业务逻辑不是很复杂时,我们要处理的不过是 CRUD 操作,这时我们可能将 dao 层与 service 层合并为一层,尽管很多人会这样做,但我仍倾向于将两层分开;因为 service 与 dao 不是一一对应的,从复用及逻辑清晰的角度考虑,应该将它们分开。在三层架构下,对于 web 层, service 层, dao 层我们都该怎么测试?这里我将介绍基于 Spring , Hibernate 和 DbUnit 的情况下我的测试方法。由于使用了 Spring ,事务管理就不在 dao ,因此要单独地测试 dao 可能要麻烦一些;另一方面, dao 中的操作大多是简单的,也不是很值得测试。在使用了 Hibernate 和 Spring 的情况下,我们要测试的除了 HQL ,还有其配置文件,我觉得对数据持久化的测试最好定在 service 上。如果 service 业务逻辑复杂的话,与数据持久化无关的业务逻辑(应该写在领域对象中)可以单独测试 ,在保证与数据持久化无关的业务逻辑的正确性下,带上 dao 操作做集成(单元)测试。

      

在使用 Spring 做测试时,推荐测试类继承自 Spring 的 AbstractTransactionalDataSourceSpringContextTests ,这也是官方推荐的做法。 AbstractTransactionalDataSourceSpringContextTests 对测试方法提供了事务管理,同时它的依赖注入特性也方便测试类注入被测试类(但我并不认为它的事务管理是很需要的)。承接于上一篇文章中的例子 ,下面给出测试类 AccountServiceTest。


package  hibernatesample.service;
import  hibernatesample.domain.Account;
import  java.io.File;
import  java.io.InputStream;
import  java.util.List;
import  org.dbunit.IDatabaseTester;
import  org.dbunit.JdbcDatabaseTester;
import  org.dbunit.dataset.IDataSet;
import  org.dbunit.dataset.xml.FlatXmlDataSet;
import  org.springframework.test.AbstractDependencyInjectionSpringContextTests;
import  org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;

public   class  AccountServiceTest  extends  AbstractTransactionalDataSourceSpringContextTests {

    
private  AccountService accountService;
    
private  IDatabaseTester databaseTester;

    
public  AccountService getAccountService() {
        
return  accountService;
    }
    
    
public  AccountServiceTest() throws  Exception{
    
    }

    
public   void  setAccountService(AccountService accountService) {
        
this .accountService  =  accountService;
    }

    @Override
    
protected   void  onSetUp()  throws  Exception {
        databaseTester 
=   new  JdbcDatabaseTester( " com.mysql.jdbc.Driver " ,
                
" jdbc:mysql://localhost/HibernateSample " ,  " root " ,  " 0102 " );
     IDataSet dataSet 
=  getDataSet();
     databaseTester.setDataSet( dataSet );
     databaseTester.onSetup();
    }

    @Override
    
protected   void  onTearDown()  throws  Exception {
        databaseTester.onTearDown();
    }

    
protected  IDataSet getDataSet()  throws  Exception {
        String path 
=   " hibernatesample " + File.separator + " dao " + File.separator + " dataset " + File.separator + " Account.xml " ;
        InputStream in 
=   this .getClass().getClassLoader().getResourceAsStream(path);
        
return   new  FlatXmlDataSet(in);
    }
    

    @Override
    
protected  String[] getConfigLocations() {
        
        
return   new  String[]{ " classpath:service-applicationContext.xml " };
    }
    
    
public   void  testInsert() {
        Account a 
=   new  Account();
        a.setName(
" aa " );
        accountService.insertAccount(a);
        List
< Account >  l  =  accountService.findAllAccount();
        assertEquals(
3 , l.size());
        Account b 
=  l.get( 2 );
        assertEquals(
" aa " , b.getName());
    }

    
public   void  testFindAll() {
        List
< Account >  l  =  accountService.findAllAccount();
        assertEquals(
2 , l.size());
        Account a 
=  l.get( 0 );
        assertEquals(
new  Long( 1 ), a.getId());
        assertEquals(
" kafka " , a.getName());
        Account b 
=  l.get( 1 );
        assertEquals(
new  Long( 2 ), b.getId());
        assertEquals(
" 0102 " , b.getName());
    }
}

        由于 AccountServiceTest 继承了 AbstractTransactionalDataSourceSpringContextTests ,因此使用 D b Unit 时就不能继承 DBTestCase ,这里采用的方法就是在 onSetUp() 中实现数据集的装入。由于 AbstractTransactionalDataSourceSpringContextTests 最第 N 个顶层的父类继承于 Junit ,所以 onSetUp() 将在 SetUp() 中被调用,默认的 onSetUp() 实现为空; onTearDown() 的原理和 onSetUp() 相似。由于 AbstractTransactionalDataSourceSpringContextTests 具有依赖注入的特性,所以被测试对象 accountService 可以通过 setter 方法获得;当然,我们也可以在构造函数中通过 Bean 工厂获得 accountService 。 protected String[] getConfigLocations() 返回的数组为 Spring Beans 配置文件的位置。

        对于与测试相关的其他类,这里就不给出了。通过使用 DbUnit ,我觉得测试持久化操作时方便了许多。不爽的地方在于,在使用了 Spring , Hibernate 的情况下,测试一个方法有些耗时,这自然不单单是 DbUnit 的问题。但如果我将 dao 和 service 的测试放在一起来做,这种耗时也只能忍着。再说一下 web 层的测试,我当前使用的 web 框架是 Webwork ,在测试 Action 的时候,对于 Web 请求之类使用 Mock ,对于 Service 的调用有时 Mock 有时真实调用。 Mock 的好处是耗时能少一些,但写 Mock 代码也很繁琐,而对 service 的 Mock 有时本身就失掉了测试的意义。

posted on 2007-06-14 09:18 kafka0102 阅读(1526) 评论(0)  编辑  收藏 所属分类: TDD