ssh单元测试

来源:互联网 发布:深圳中航软件 编辑:程序博客网 时间:2024/04/26 16:52

通常我们的bs模式应同都是5层构架体系的:DAO  BL  Action Taglib JSP
在这5层之中,只有jsp是非java代码的,所以也是比较难以进行单元测试的一层
而且jsp作为表现层来说呢,通常变化也比较大。所以对jsp编写代码测试的代价要远大于人工直接对页面样式进行测试。

这篇文章主要将针对除去jsp以外的另外4层相关单元测试的基本框架进行了一些讨论:
首先我们要在单元测试前问自己一个问题:单元测试的目的究竟是为了什么?
为了让我们的项目更加时髦?为了让我写的代码没有bug?
我个人觉得单元测试的目的就是为了能够用轻易的重复对代码的测试!

下面我将对每一层的目的是什么,单元测试的目的是什么,还有改层单元测试的相关问题进行讲述

1、DAO层
DAO的目的:根据指定的参数对相关的数据对象进行处理
                         基本操作有查询操作  对数据源无影响 有返回值 
                                             增改操作 影响数据源 无返回值
                                             删除操作 影响数据源 无返回值

从上面我们可以看出,DAO的test主要是针对DAO的3种操作进行测试
对于查询操作我们可以从返回的结果集来进行判断
而对于增改操作和删除操作我们就是只能在方法调用后对数据源进行判断了

这里,现在一般DAO层我们都会使用Spring+hibernate来进行构建,那么一个DAO的实例就需要引入到spring和DAO的相关配置了。而且由于牵扯到外部环境,所以这里外部环境的处理可以采用两种方式,一种是直接使用数据库,这里就需要有一个真实的数据源存在了二是模拟一个数据源,比如使用内存数据库或者文本数据库来建立一个模拟环境,方便单机进行测试

而且由于DAO是由Spring来进行管理的,所以在测试的时候需要加载Spring的上下文来获取dao的bean,关于spring的加载我使用的是spring-mock,它的好处是在事物处理后可以对相关的数据库操作进行rollback

2、BL层
BL层的目的:根据业务逻辑对业务处理进行封装
以往其实我们有时候习惯把一些业务逻辑代码放置到上层DAO或者下层Action,taglib中
所以我总结了一下,基本的业务逻辑有:
参数的验证和判断
业务逻辑错误处理
业务规则判断

所以针对于这些功能我们可以把原本属于BL的代码归还给BL了,然后针对于bl的几个功能就可以清晰地编写单元测试了,这时候一般会用到做单元测试时比较有用的一个副产品:重构。而这个副产品的价值将会在你重构后体现出来,DAO,BL,ACTION,taglib的代码职责更加明晰了,该做什么判断的就座什么判断

BL这层一般也是用Spring来进行管理的,所以我们还是需要加载spring的上下文,当然也可以不加载Spring,然后根据接口模拟一个DAO出来,但是我一般觉得这种模拟花消比较大,这个代价是否值得一直是我所怀疑的

3、Action层
action层的目的:对于请求进行跳转控制
一个控制层方法的主要功能有:获取参数,调用BL对参数进行执行,根据执行的结果进行页面参数赋值,跳转到结果页面
当然上面除了跳转的到结果页以外其他功能都不是必须的

针对于上述功能action的单元测试基本框架是:封装请求参数,发送请求,判断所需的页面bean和跳转的结果页面是否正确
这里牵扯到对于请求环境的模拟,一般不同的前台构架会有不同的模拟方法,struts下我使用的是strutstescase,它可以较为完整的读取Struts配置文件

当然一般在action中会用到Spring的上下文获取bl,由于之前已经对bl进行了测试确认,所以我建议可以直接使用实际的bl,而不是再花费代价开发一个模拟的BL

4、taglib层
taglib层目的:根据参数在页面上按照固定的样式输出
一个标签的基本功能有:获取参数,调用BL对参数进行执行,把执行结果放到指定样式中,把得到的页面代码输出

相对来说获取参数,调用BL对参数进行执行,把得到的页面代码输出,比较固定,测试的意义不大,所以主要测试的就是把执行结果放到指定样式中这个功能。这个功能通常就是给一个结果集参数然后组合返回一个StringBuffer,所以测试起来也比较容易

但是taglib在使用的时候也需要的Spring甚至Struts的配置,这就需要在初始化的时候把相关环境也要加载进来

这里大家因该可以看出我的单元测试并不是完全独立的,而是伴随渐进的按照DAO,BL,Action,taglib这样的顺序引入环境,这样一个可以减少测试开发的代价,一个是可以增加测试的准确性

 

 

领域层 

     起初我们使用TDD的时候,从领域模型开始,两个人先在纸上画出领域模型的草图,写上一些显而易见的方法,然后打开Eclipse开始写JUnit单元测试,Eclipse对于TDD的支持很好,我们按照特性描述(就算是UserStory吧),开始创建每个对象,当然会抱错,然后使用Eclipse自动创建那个类,我们会根据需求加入责任,然后自动生成methodstub。我们花了两个小时创建了6个对象,完成了一个特性。当然,对于领域层的测试,这一切都很正常。

      然后我们引入了Springframework,我们先设计一些接口,然后在Spring的配置文件中映射好实现这些接口的对象,然后我们在测试用例中写上spring初始化bean的代码:

 

java 代码
  1. ApplicationContext ctx = new ClassPathXmlApplicationContext( new                   String[] { "applicationContext.xml" });   
  2. service=(BussinessFacade) ctx.getBean("service");  

 

          我们在每一个测试方法里都写,后来知道应该把这样的代码放到setUp中。我又开始使用Spring的测试框架,但是这样运行单元测试实在是太慢了!后来,知道了测试领域层的时候不要混入框架,创建对象直接用new就好。

表现层  

        我来继续观察表现层,其中包括StrutsAction作为控制器的控制层,和Ext+HTML的UI。UI已经是一个独立的“系统”了,唯一和它联系的就是Action了。Action的作用是接收HttpRequest,读取参数,调用业务门面,拼接并返回Ext需要读取的数据源(XML或JSon)。我们采取的Struts和Spring的整合策略是将Action托管给Spring。这样,如果想要测试Action:

        1、和Spring框架一起来测试

        2、Action中的逻辑很简单,(其实Facade中封装了领域对象的协作,也可看作一种控制器)测试的目的是:

            1)看看参数传过来没有

            2)领域模型是不是返回了正确的值

            3)XML或JSon的返回串拼接的是否正确。

            4)还有更基础的是测试一下Struts和Spring的配置文件写的对不对。

        3、实际上返回的领域对象只是Mock Object。

       我们的测试很原始,还没有使用过StrutsActionTest这样的框架,只是直接使用Tomcat来测试(不过已经比直接使用WebLogic容易多了)。

     速度很慢,每次都要初始化那些XML配置文件,而且还要读根本用不上的Hibernate的配置文件。

  关于Hibernate配置文件的问题是这样的:

 

xml 代码
  1. <bean id="sessionFactory"  
  2.     class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  3.     <property name="configLocation">  
  4.         <!-- <value>WEB-INF/classes/Account-Hibernate.cfg.xml</value>-->  
  5.           <value>Account-Hibernate.cfg.xml</value>    
  6.     </property>  
  7. </bean>  

       我们不得不在进行DAO测试时,使用没有注释掉的value来读取cfg.xml。而在测试Action时,又需要使用注释掉的配置。   

      我觉得或许采用下面的策略能好些,不过不知道各位在自己的项目中都是如何做的:

       测试表现层时用专为测试这一层的Spring配置文件(我们采用了org.springframework.web.context.ContextLoaderListener这种方式),避免和Hibernate配置文件挂上联系。

 

持久层

        采用DAO模式,我们的DAO很简单,就是直接调用Spring的getHibernateTemplate的方法,于是测试的时候关键就是测试:

     1、HQL语句是否书写正确,SQL语句是否生成正确

     2、Hibernate映射是否写的正确

     3、Sping和Hibernate基础设施是否配置正确

     4、是否按照预期完成CRUD操作。

     测试一个DAO需要耗时2秒,而不是0.12秒。不过如果基于上面的目的进行测试也无可厚非。

原创粉丝点击