动态代理在单元测试中的运用

1. 问题的提出      

        单元测试过程对于软件系统质量的提高有着非常的重要的作用,如果能对流程分支做到有效的覆盖再结合每日构建的自动测试,将对故障的迅速收敛有着很大的帮助。但是实际应用中,发现单元测试对象很多情况下对环境的依赖非常大。很多时候没有办法测试,因此单元测试在实际过程中起到的作用非常有限,仅仅对一些比较独立的算法逻辑可以测试。这成为困扰单元测试人员的主要问题。
比如一些被测试方法的入参是和环境密切相关的,如数据库连接Connection,结果集ResultSet等以及一些界面元素这些都很难模拟,单元测试时,这些参数的构造是非常困难。     

2。 解决思路 *

         动态代理通过运行期拦截对被代理对象方法的调用可以很好的用于应对这类单元测试中对环境依赖性很强的类。其中可以直接使用JMock测试包来Mock 这类对象并通过定义一些约束条件和返回值达到数据模拟和测试目的。如果不能使用JMock也可以直接使用CGLIB结合Objenesis来构造一些Mock对象,通过实现拦截器完成模拟对象。在系统管理模块中作者通过二者结合,很好的完成了代码可测试性改造和测试代码的构建。

      从JDK1.3开始,java支持动态代理。动态代理可以实列化一个inteface,并在运行期拦截某些方法的调用。不过JDK提供的动态代理机制只能够处理interface接口,对于抽象类和一般类无能为力。

 

创建某一接口 Foo 的代理: 
     InvocationHandler handler 
=   new  MyInvocationHandler(...);
     Class proxyClass 
=  Proxy.getProxyClass(
         Foo.
class .getClassLoader(),  new  Class[]  { Foo.class } );
     Foo f 
=  (Foo) proxyClass.
         getConstructor(
new  Class[]  { InvocationHandler.class } ).
         newInstance(
new  Object[]  { handler } );
 
或使用以下更简单的方法: 
     Foo f 
=  (Foo) Proxy.newProxyInstance(Foo. class .getClassLoader(),
                                          
new  Class[]  { Foo.class } ,
                                          handler);


  

CGLIB是一个开源的api,它可以对抽象类和一般的类实现动态代理,拦截指定方法的调用。但它同样有一个不足之处:要求被代理的类有public的构造方法,对于一些私有构造方法的类同样无能为力。


 

  private   static  Object newInstance(Class clazz)  {
        
try {
            MethodInterceptorImpl interceptor 
= new MethodInterceptorImpl();
            Enhancer e 
= new Enhancer();
            e.setSuperclass(clazz);
//指定父类
            e.setCallback(interceptor);//设置拦截器
            
//针对目标对象的构造方法
            Object bean = e.create(new Class[]{String.class},
new String[]{callerClassName});
            
return bean;
        }
 catch (Throwable e) {
            
throw new Error(e.getMessage());
        }


       Objenesis是专门用于实例化一些特殊java对象的一个工具,如私有构造方法,带参数的构造等不能通过class.newInstance()实例化的,通过它可以轻松完成。

 

Objenesis objenesis  =   new  ObjenesisStd();
// 实例化一个class,每一个实例化的class都是实现了Factory接口。
Factory proxy  =  (Factory)objenesis.newInstance(proxyClass);
// 设置拦截器
proxy.setCallbacks( new  Callback[]  {invokeHandler} );

 
在单元测试过程中,通过CGLIB和Objenesis的组合可以完成对所有java类的动态代理。JMock是基于JUnit框架,使用动态代理来Mock测试对象需要的一些环境参数的一个开源API。在单元测试中使用上面办法Mock一些难以构造的环境参数,可以大大扩展单元测试的范围,平时一些不能测试的方法变得可行了。
       因为JMock1.2还是不能支持私有构造类的Mock,建议使用JMock2.2以上,这要求编译环境使用JDK1.5以上。某些情况下只能使用JMock1.2结合CGLIB和Ojbenesis完成。
 
3。效果评价*
通过对代码的改造,测试的代码的编写,顺利地完成了对功能点的测试,达到预期结果,有效地覆盖到各个分支。测试代码的独立存在,不会影响到具体功能的实现,对系统本身不会造成影响。
总结起来:
1.       对现有代码改造影响很小。
2.       可以对环境依赖性很强的逻辑代码进行测试。
3.       对数据模拟更加简单方便。
最为重要的是:使用动态代理可以有效的增加单元测试的覆盖面,使测试变得更加易行。
 
4.             推广建议
使用动态代理在不破坏现有代码的情况下,可以很好的解决被测试代码与环境强偶合性问题。很大程度的增加单元测试的可测试范围,真正意义上使得单元测试名副其实。对于使用Java构建的软件系统有着非常大的推广价值。
6.参考资料
       1. JMock的官方网站    http://www.jmock.org/     
       2. CGLIB介绍 http://cglib.sourceforge.net/howto.html
       2.java doc http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Proxy.html
       3.王东刚 软件测试与Junit实践,人民邮电出版社,2004年1月
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值