再谈Mock Object
发现静寂的夜能让我的思维加快。
用Mock Object进行Unit Test已经一周多了,发现以前对Mock Object还是很肤浅,即使是现在我也不敢说我对Mock Object的理解就一定正确。
这篇blog假设你已经熟悉JUnit、了解Mock和TDD。
如果你是直接开始使用JMock 、Easy Mock或者是MockMaker等Mock Object框架的,我建议你简单了解一下Mock Object的演化历史,这样你在使用Mock Object时才会更有的放矢。
Mock object有关键的两个概念:
* 建立起环境的概念。在www.mockobjects.com的faq中有一句是这样叙述的“I think the fundamental thing to remember about Mock objects is that they are just that – simple shells or placeholders.” Mock object只是替代了被测Object环境的代码。
* test assertions被隐藏在Mock object内部实现中了,在你的test case中用来verify被测代码与Mock object的交互。
在单元测试中,人们发现有一些问题(这些问题在我的“认识Mock Object”中已列出)常见单元测试工具(如JUnit)并不能很好、很便捷的解决,这样Mock object被引入来解决这些问题。
最初人们手工编写Mock Object。随着测试问题越来越复杂,人们自己手工编写的Mock object越来越多,人们开始将编写Mock Object过程中一些通用的东西抽象出来,形成了一些Mock Object Lib,以帮助开发人员快速得到自己需要的Mock Objects。
当前的Mock Object Lib有多种,大致可分为两类:
* Static Mock Objects Lib
* Dynamic Mock Objects Lib
这里简单举例说明一下不同类型Mock object lib的使用方法,并与手工编写进行对比。
问题:我们在测试某个class时,我们需要与MyInterface这个接口进行交互,而该接口尚未实现,这时我们使用Mock object来替代。
接口MyInterface:
public interface MyInterface {
public SomeClass getSomething();
public void setSomething(SomeClass aSomething)
// Other methods omitted…
}
* 手工编写mock object:
public class HandcraftMockMyInterface implements MyInterface {
public SomeClass getSomething() {
return something;
}
public void setSomething(SomeClass aSomething){
this.someting = aSomething;
}
private SomeClass something;
//others
}
* 使用Static Mock Object Lib编写Mock Interface:
public class StaticMockMyInterface extends MockObject implements MyInterface{
private final ExpectationValue something = new ExpectationValue("something");
public void setSomething(SomeClass something){
this.something.setActual(something);
}
public void setExpectedSomething(SomeClass something){
this.something.setExpected(something);
}
public void SomeClass getSomething(){
return this. something;
}
}
* 使用Dynamic Mock Object Lib编写Mock Interface:(以JMock为例)
Mock dynamicMockMyInterface = new Mock(MyInterface.class);
MyInterface mi = (MyInterface) dynamicMockMyInterface.proxy();
SomeClass someThing = new SomeClass();
dynamicMockMyInterface.expects(once()).method(“setSomething”).with(eq(someThing));
dynamicMockMyInterface.expects(once()).method(“getSomething”).will(returnValue(someThing));
//执行你的测试代码,比如你的tested object与MyInterface mi交互的代码。
dynamicMockMyInterface.verify();
Mock Object的使用流程
- Setup any state — setup the fixture for your test
- Set expectations for the test
- Run the target code
- Verify that your expectations have been met
Mock Object Practical Experience
- 好的设计是容易测试的设计
- 尽量让测试在内存中完成,不要在硬盘上留下“垃圾”
- 面向接口使用Mock Object。better to implement interface , not inherit class
评论