单元测试


比如下面的代码:


class ForTest{
public void doSomething(){
TaskExecutor task = new TaskExecutor(taskId);
ProcessEngin processEngin=new ProcessEngin();
//任务发起人
String rwfqr = task.getTaskFqr();
long processId = 0;
try {
// 启动任务。
processId = processEngin.startProcess(rwfqr,ProcessConstants.SSGLY_QD_EVENT, taskId);
// 发送任务。
processEngin.doProcess(realReveiver,ProcessConstants.SSGLY_FS_EVENT, processId);
} catch (ProcessEnginException ex) {
throw new RunTaskException("error", ex);
}
}
}

OK 平时的代码都这么写的。有什么问题么?当然没什么问题,肯定能运行得很好,而且看上去还挺简洁。
可是怎么测试呢?
我们用Junit为它写个TestCase

class TestForTest extends TestCase{
public void testDoSomething(){
ForTest test=new ForTest();
test.doSomething();
.......
}
}

就上面这个样子了 然后怎么办呢?断言在哪里呢?这样就有无处着力的感觉了。
上面的例子还好有可以抛出一个异常我们可以这样写:

class TestForTest extends TestCase{
public void testDoSomething(){
ForTest test=new ForTest();
try{
test.doSomething();
}catch(RuntimeException ex){
fail("测试异常。");
}
}
}

上面的测试代码可以判断processEngin的startProcess()个doProcess()方法有没有异常。
但是如果方法都有返回值得话那测试起来似乎方便些。

但是如果你不知道ProcessEngin类和TaskExecutor类或者它们还没写完 那上面的那个测试用例是行不通的。
那如何测试呢?
通常用MockObject方法来构造一个不知道具体实现,但是知道它大概形式的类或者接口。
比如对TaskExecutor 从代码中可以看出 它肯定有一个getTaskFqr()这样的方法,于是我们这样Mock一个

class MockTaskExecutor extends TaskExecutor{
public String getTaskFqr(){
return "stone";
}
}
class MockProcessEngin extends ProcessEngin{
public long startProcess(String arg1,String arg2,String arg3){
return 1;
}
public void doProcessEngin(String arg1,string arg2,String arg3){
....
}
}

然后我们可以用MockTaskExecutor的对象代替TaskExecutor的对象,MockProcessEngin对象代替ProcessEngin对象.
但是ForTest的doSomething方法还是不能测试的,因为TaskExecutor和ProcessEngin对象都被声明为局部变量.于是我们要重写ForTest

class ForTest{
private TaskExecutor task;
private ProcessEngin processEngin;
public void doSomething(){
//任务发起人
String rwfqr = getTaskExecutor().getTaskFqr();
long processId = 0;
try {
// 启动任务。
processId = getProcessEngin().startProcess(rwfqr,ProcessConstants.SSGLY_QD_EVENT, taskId);
// 发送任务。
getProcessEngin().doProcess(realReveiver,ProcessConstants.SSGLY_FS_EVENT, processId);
} catch (ProcessEnginException ex) {
throw new RunTaskException("error", ex);
}
}
public void setTaskExecutor(){
......
}
......
}

这样我们就可以测试了

class TestForTest extends TestCase{
public void testDoSomething(){
ForTest test=new ForTest();
MockTaskExecutor task=new MockTaskExecutor();
MockProcessEngin processEngin=new MockProcessEngin();
test.setProcessEngin(processEngin);
test.setTaskExecutor(task);
try{
test.doSomething();
}catch(RuntimeException ex){
fail("测试异常。");
}
}
}

OK 完了.
当然如果一直MockObject测试的话,编写Mock对象似乎很累,很烦。现在有些好的工具 比如EasyMock等,不过EasyMock仅Mock接口。
话说回来用接口编程是个好的编程习惯。

详细mock测试见如下

import junit.framework.TestCase;
import org.easymock.MockControl;
import cn.com.jdlssoft.daup.app.analysis.object.IBeanObject;
import cn.com.jdlssoft.daup.app.analysis.object.ZbScore;
import cn.com.jdlssoft.daup.app.semantic.ISemParser;

public class AbsMarkFunctionTest extends TestCase {

/**
* 对public ZbScore mark(IBeanObject zbBean, int zbQz)方法进行测试。这个方法
*


* 中需要调用IBeanObject.getBeanPropValue(String)和ISemParser.calculate(String)两个方法。
*


* 为了专心测试mark方法,而避免另外两个类带来的干扰,采用EasyMock进行测试。该类主要演示EasyMock的测试方法
*


* easymock的测试原理是:按顺序录制动作和返回值,然后回放。具体看类中注释。
*/
public void testMarkIBeanObjectInt() {
ZbMarkInfo zbInfo = new ZbMarkInfo();
zbInfo.setZbdm("test");
zbInfo.setMarkFunction("testFunc");
AbsMarkFunction af = new AbsMarkFunction(zbInfo) {
public void clear() {
}
};
// 第一步:用将要mock的对象创建mock控制器。若不是接口,
// 则使用(MockControl parserControl1 =
// MockClassControl.createControl(AbsMarkFunction.class);
MockControl parserControl = MockControl.createControl(ISemParser.class);
// 第二步:从mock控制器中得到mock后的对象。
ISemParser parser = (ISemParser) parserControl.getMock();
// 第三步:录制动作并且设置返回值。
parser.calculate("testFunc");
parserControl.setReturnValue("50");
// ---以上第三步所录制的意思是:执行parser.calulate("testFunc")时返回50----
// 第四步:准备回放。
parserControl.replay();
// 下面是第二个对象的mock过程
MockControl beanControl = MockControl.createControl(IBeanObject.class);
IBeanObject curBean = (IBeanObject) beanControl.getMock();
curBean.getBeanPropValue("test");
beanControl.setReturnValue("100");
beanControl.replay();
// --第二个对象mock结束
af.setMarkParser(parser);
// 开始用上述mock出来的对象进行测试。mark方法中调用parser.calculate("testFunc")
// 返回50;调用curBean.getBeanPropValue("test")返回100
ZbScore score = af.mark(curBean, 50);
// 确认录制的动作都进行了测试。
parserControl.verify();
beanControl.verify();

assertEquals(new Double(100), new Double(score.getZbValue()));
assertEquals("25", String.valueOf(score.getZbScore()));
}
}


没有评论: