打桩(mock)是单元测试的重要内容和难点,学好打桩的技术,做单元测试基本就没什么困难了。
mock有两种,一种是静态的,一种是动态的。静态的就是在写测试代码之前根据需要打桩的类生成另外一个类,这个类就是mock
object。动态的就是mock
object是在测试代码运行的时候才生成的。所以很明显,动态打桩比静态打桩要方便地多。本章就是介绍动态打桩的工具。
早期的动态mock工具只能够mock接口,而不能够mock类;现在的mock工具无论是mock接口还是类都能够轻松完成了。
easymock就是其中的佼佼者,easymock现在已经是2.2版本了,本文使用的是easymock1.2_Java1.5版本。使用
easymock能够轻松的mock任何接口,但如果想mock类,那还需要另外一个lib,就是easymockclassextension,使用了
这个工具,你就能轻松地mock你要的任何类了。本文使用的是easymockclassextension1.2,使用这个库必须要cglib这个
jar包,而cglib又需要asm的jar包,所以要搭建好环境还得花些时间,不过当你把环境搞好之后,你便会发现物超所值,从此以后打桩无难事了。
cglib和asm的版本一定要适配,要不然不能正常工作,幸好cglib的网站已经提供了cglib和asm的绑定包,本文所用的是cglib-
nodep-2.2_beta1版本。
本章先对如何对接口进行打桩示例,下一章再对如何对接口进行打桩示例。
先看欲打桩接口代码:
**********************************************************
* Project Name: lhjTest
* File Name : CoolInterface.java
* File Desc : CoolInterface.java
* Author : Administrator
* Create : 2007-3-25
* Modify:
**********************************************************
*/
package
org.lhj.cool.junit;
/**
*
@author
Administrator
*/
public
interface
CoolInterface
{
String reverseString(String inputStr)
throws
NullPointerException;
}
该接口很简单,只要一个reverString的方法。
下面看看欲测试类(使用了CoolInterface)的代码:
**********************************************************
* Project Name: lhjTest
* File Name : UseCoolInterface.java
* File Desc : UseCoolInterface.java
* Author : Administrator
* Create : 2007-3-25
* Modify:
**********************************************************
*/
package
org.lhj.cool.mock;
import
org.lhj.cool.junit.CoolInterface;
/**
*
@author
Administrator
*/
public
class
UseCoolInterface
{
private
CoolInterface cool;
public
void
setCoolInterface(CoolInterface cool)
{
this
.cool
=
cool;
}
public
String reverseString(String inputStr)
throws
NullPointerException
{
return
cool.reverseString(inputStr);
}
}
这个类也很简单,相当于一个adapter。
下面看看如何使用easymock进行接口的打桩:
**********************************************************
* Project Name: lhjTest
* File Name : UseCoolInterfaceJTest.java
* File Desc : UseCoolInterfaceJTest.java
* Author : Administrator
* Create : 2007-3-25
* Modify:
**********************************************************
*/
package
org.lhj.cool.mock;
import
junit.framework.TestCase;
import
org.easymock.MockControl;
import
org.lhj.cool.junit.CoolInterface;
import
org.lhj.cool.mock.UseCoolInterface;
/**
*
@author
Administrator
*/
public
class
UseCoolInterfaceJTest
extends
TestCase
{
private
UseCoolInterface testInterface;
private
MockControl ctrl;
private
CoolInterface cool;
/*
(non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
protected
void
setUp()
throws
Exception
{
super
.setUp();
ctrl
=
MockControl.createControl(CoolInterface.
class
);
cool
=
(CoolInterface) ctrl.getMock();
testInterface
=
new
UseCoolInterface();
testInterface.setCoolInterface(cool);
}
/*
(non-Javadoc)
* @see junit.framework.TestCase#tearDown()
*/
protected
void
tearDown()
throws
Exception
{
super
.tearDown();
}
/**
* Test method for {
@link
org.lhj.cool.mock.UseCoolInterface#reverseString(java.lang.String)}.
*/
public
void
testReverseString()
{
ctrl.expectAndThrow(cool.reverseString(
null
),
new
NullPointerException());
ctrl.expectAndReturn(cool.reverseString(
""
),
null
);
ctrl.expectAndReturn(cool.reverseString(
"
AAaa
"
),
"
aaAA
"
);
ctrl.expectAndReturn(cool.reverseString(
"
aaAA
"
),
"
AAaa
"
);
ctrl.replay();
try
{
testInterface.reverseString(
null
);
fail();
}
catch
(NullPointerException e)
{
//
donothing
}
assertNull(testInterface.reverseString(
""
));
assertEquals(
"
aaAA
"
, testInterface.reverseString(
"
AAaa
"
));
assertEquals(
"
AAaa
"
, testInterface.reverseString(
"
aaAA
"
));
ctrl.verify();
}
}
从上面可以看出使用easymock的步骤:
1、在setUp时创建MockControl,它是控制mock object行为的类,参数传入欲打桩的接口的class。
2、从mockControl中getmock,返回mock object转化为本类型对象。
3、创建欲测试类并将mock object传进去。
以上几步代码为:
=
MockControl.createControl(CoolInterface.
class
);
cool
=
(CoolInterface) ctrl.getMock();
testInterface
=
new
UseCoolInterface();
testInterface.setCoolInterface(cool);
4、使用mockControl对mock object进行设置,这一步又称为训练,代码为
null
),
new
NullPointerException());
ctrl.expectAndReturn(cool.reverseString(
""
),
null
);
ctrl.expectAndReturn(cool.reverseString(
"
AAaa
"
),
"
aaAA
"
);
ctrl.expectAndReturn(cool.reverseString(
"
aaAA
"
),
"
AAaa
"
);
第一行意思是告诉mockControl,mock
object的reverseString方法会被调用,传入的参数是null,并会抛出异常;第二行意思是告诉mockControl,mock
object的reverseString方法会被调用,传入的参数是"",并返回null;其他行类似。
5、训练完之后可以接受检查了,代码为:
6、现在可以进行测试了,以下是测试代码:
try
{
testInterface.reverseString(
null
);
fail();
}
catch
(NullPointerException e)
{
//
donothing
}
assertNull(testInterface.reverseString(
""
));
assertEquals(
"
aaAA
"
, testInterface.reverseString(
"
AAaa
"
));
assertEquals(
"
AAaa
"
, testInterface.reverseString(
"
aaAA
"
));
可以看出,这跟一般的JUnit语句是一样的。
7、检验测试结果
mockControl会验证mockObject是否按照训练的情况参与了测试,即训练时的方法是否被调用了,传入的参数对不对。
以上示例只是easymock的一部分功能,除此之外,easymock还能测试mockObject的方法是否按照顺序调用了,调用的次数对不对,传入的参数是否在给定的范围之内,另外还可以改变参数对象的内容。
除了easymock可以mock接口外,像mock objects,Jmock,mock
Creator都可以实现此类功能。其中mock Creator还可以mock class,但是可惜mock
creator是静态打桩的,实现起来有额外的工作。我在公司里用的是jdk1.4,无法用easymockclassextension1.2,所以使
用了easymock加mock creator,幸好mock creator有eclipse插件,生成mock
class也比较方便。所以如果你使用的是JDK1。4,用easymock加mock
creator绝对是最佳组合;如果你使用的是JDK1.5,那么使用easymock加easymockclassextension会使你工作更轻松