随笔 - 4  文章 - 16  trackbacks - 0
<2008年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用链接

留言簿(3)

随笔分类

随笔档案

文章分类

新闻档案

好友

最新随笔

搜索

  •  

积分与排名

  • 积分 - 11131
  • 排名 - 2283

最新评论

阅读排行榜

评论排行榜

本节是单元测试系列的第二篇。重点讲解如何使用Mock/Stub和依赖注入技术进行单元测试。关于工具JUnit等则不做累赘介绍。 希望通过本章能够帮助大家开始单元测试的有益实践,与大家共勉!

单元测试(技能篇)

一、Stub技术

这是最为古老的一种测试技能。通过类层次上的替换实现了对待测环境的模拟。

实现的时候有两种途径:

1、重写实际类,在测试时,先于实际类加载,即覆盖。如:我们在unittest/stub文件夹下针对于每一个重写类都有相同的包结构和类名:


在类路径中优先加载:


2、在实际代码中添加判断。比如,如果当前是测试环境if(isUT)执行XX操作,截断真正需要做的事。

    publicvoid sendCommand(int cmdCode)

    {

       if(isUT())

       {

           //...

       }

       else

       {

           //...

       }

    }

Stub技术的问题就在于我们在重写这些类的时候,不仅仅要关注接口,还要关注其内部逻辑。如果你只是简单的返回一个固定的响应,会很简单。但是对于每一次运行需要根据不同的输入返回不同的输出时方法内部的处理就会复杂的多。

由于实现的难度,所以,使用时就要注意:有高价值、重用度高、数量少。这就是说,重写一个类,就可以有一大批类可以用。

二、Mock技术

Mock是目前单元测试中最常用的。用来在对象层次上实现细类度替换十分方便。

当我们在测试中,需要其它类/接口的一个方法时,我们可以通过继承/实现其一个子类对象来替换实际对象。在Mock子类中将需要的方法直接返回需要的结果就行了。

    privateclass Mock_QueryCtrl extends QueryCtrl

    {

       public List queryNEList()

       {

           List neList = new ArrayList();

           //直接填充并返回你需要的数据...

           return neList;

       }

    }

同样,我们也可以通过测试待测类的子类来测试待测类。这对于被测方法使用了自身类的方法时很适用。

三、依赖注入

单元测试的一个关键就是替换。类层次上的替换,通过在类路径中提前加载就可以实现。而在对象层次上,java的反射机制提供了很好的帮助。

1.获取/注入私有属性

2.执行私有方法

附:注入私有属性的实现:

    publicvoid setFieldObject(Object instance, String fieldName, Object value)

           throws IllegalArgumentException, IllegalAccessException,

           NoSuchFieldException {

       Field field = null;

       Class c = instance.getClass();

       do {

           try

           {

              field = c.getDeclaredField(fieldName);

           } catch (SecurityException e)

           {

              e.printStackTrace();

           } catch (NoSuchFieldException e)

           {

              c = c.getSuperclass();

           }

       }

       while (c.getName() != "java.lang.Object" && field == null);

       if (field != null)

       {

           field.setAccessible(true);

           field.set(instance, value);

       }

       else

       {

           thrownew NoSuchFieldException(fieldName);

       }

    }

注:这是一个简单实现,实际中需要优化。

四、实例:

下例演示了如何测试类NEListTableShowNETable()方法。其中注意的是,方法中调用了类QueryCtrlqueryNEList()方法。

待测类:

publicclass NEListTable

{

    QueryCtrl ctrl = null;

   

    publicvoid ShowNETable()

    {

       List neList = ctrl.queryNEList();

      

       for(int i = 0;i<neList.size();i++)

       {

           //neList转换为表格行

       }

      

       //显示表格...

    }

}

publicclass QueryCtrl {

    public List queryNEList()

{

       returnnull;

    }

}

测试类:

public class TestNEListTable extends TestCase

{

    private NEListTable table = null;

    private TestHelper helper = null;

    public void testShowNETable()

    {

       Mock_QueryCtrl ctrl = new Mock_QueryCtrl();

       helper.setObjectField(table,"ctrl",ctrl);//Mock对象注入table

       table.ShowNETable();

       assertTrue(table.getRowCount()>0);

    }

   

    private class Mock_QueryCtrl extends QueryCtrl

    {

       public List queryNEList()

       {

           List neList = new ArrayList();

           //返回你需要的数据...

           return neList;

       }

    }

}

posted on 2008-07-10 22:14 wukaichun 阅读(2007) 评论(4)  编辑  收藏 所属分类: test

FeedBack:
# re: 单元测试(技能篇) 2008-07-21 21:34 切尔斯基
Mock不是你说的这个样子吧, 你这里说的mock其实是stub  回复  更多评论
  
# re: 单元测试(技能篇) 2008-07-21 23:19 wukaichun
@切尔斯基

非常感谢.不知您的见解是什么?
我的理解是:Stub的替换发生在类层面,而Mock则是对象层面. 前者粒度较大,更适合功能测试级;后者粒度相对较小,对于方法级的单元测试比较有效.
  回复  更多评论
  
# re: 单元测试(技能篇) 2008-07-28 22:42 Growing
没错,同意"切尔斯基"的说法,楼主的Mock其实就是Stub,Mock与Stub的区别我认为是在于:Mock不需要像Stub那样增加类。而是通过目前使用较多的EasyMock和JMock等框架,通过动态代理等方式生成代理类,使用这个代理类代替被测试类的依赖对象。保证测试的独立、隔离。  回复  更多评论
  
# re: 单元测试(技能篇) 2008-11-19 14:54 网络监控软件
能不能介绍一些C++的单元测试框架。  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: