本节是单元测试系列的第二篇。重点讲解如何使用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);
}
}
注:这是一个简单实现,实际中需要优化。
四、实例:
下例演示了如何测试类NEListTable的ShowNETable()方法。其中注意的是,方法中调用了类QueryCtrl的queryNEList()方法。
待测类:
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;
}
}
}
|