- 浏览: 39075 次
- 性别:
- 来自: 苏州
最新评论
easymock浅析
一、EASYMOCK基本工作方式回顾
首先我们通过一个最基本的例子来回顾一下EASYMOCK的工作方式
我们有一个计算器,里面依赖一个做int加法的加法器
Calculator.java
- public class Calculator {
- private Adder adder;
- public void setAdder(Adder adder) {
- this .adder = adder;
- }
- public int add( int x, int y){
- return adder.add(x, y);
- }
- }
Adder.java
其中这个加法器的实现在别的模块中
在计算器的模块中我们调用加法器的接口
现在我们需要对Calculator进行单元测试
此时我们不想依赖Adder的具体实现来进行测试
这样Adder模块内的错误将会干扰Calculator模块的测试
造成问题定位困难
此时我们需要一个Adder接口的Mock对象
由它来响应Calculator的方法调用
这里我们可以实用EASYMOCK所提供的功能
- import org.easymock.EasyMock;
- import org.junit.Before;
- import org.junit.Test;
- import junit.framework.Assert;
- import junit.framework.TestCase;
- public class CalculatorTest extends TestCase {
- private Calculator tested;
- private Adder adder;
- @Before
- public void setUp() {
- tested = new Calculator();
- adder = EasyMock.createMock(Adder.class );
- tested.setAdder(adder);
- }
- @Test
- public void testAdd() {
- // adder in record state
- EasyMock.expect(adder.add(1 , 2 )).andReturn( 3 );
- EasyMock.replay(adder);
- // adder in replay state
- Assert.assertEquals(3 , tested.add( 1 , 2 ));
- }
- }
在setUp()中我们通过EasyMock.createMock()方法生成了一个MOCK对象
并且注入到Calculator的实例中
现在MOCK对象处于Record State下
在这个状态下,通过对MOCK对象进行方法调用
以及对EasyMock.expect() /andReturn() / andThrow()
方法的调用,我们能够记录MOCK对象的预期行为
这些行为将在Replay State中进行回放
比如上例,我们在adder对象的Record State下记录了一次
adder.add()调用,参数为1和2,返回值为3
接着在Replay State下,通过调用Calculator.add()方法
我们调用了adder对象的add()方法
EasyMock将会检查这次调用的方法和参数列表是否与已经保存的调用一致
如果一致的话,返回所保存的返回值
由于这次调用和Record State中的记录一致
上面例子中我们的test.add()将会返回3
二、MOCK对象的创建------JDK的动态代理
接下来我想通过EasyMock的源码来窥探一下Mock对象的创建过程
这条语句
adder = EasyMock.createMock(Adder.class);
调用了
in org.easymock.EasyMock
- public static <T> T createMock(Class<T> toMock) {
- return createControl().createMock(toMock);
- }
我们可以看到这里创建了一个控制器MocksControl
然后调用了MocksControl的createMock方法
in org.easymock.EasyMock
- public static IMocksControl createControl() {
- return new MocksControl(MocksControl.MockType.DEFAULT);
- }
这个MocksControl类用于管理Mock对象的状态迁移
即Record State和Replay State的转换
我们再来看看MocksControl的createMock方法
in org.easymock.internal.MocksControl
- public <T> T createMock(Class<T> toMock) {
- try {
- state.assertRecordState();
- IProxyFactory<T> proxyFactory = createProxyFactory(toMock);
- return proxyFactory.createProxy(toMock, new ObjectMethodsFilter(
- toMock, new MockInvocationHandler( this ), null ));
- } catch (RuntimeExceptionWrapper e) {
- throw (RuntimeException) e.getRuntimeException().fillInStackTrace();
- }
- }
- protected <T> IProxyFactory<T> createProxyFactory(Class<T> toMock) {
- return new JavaProxyFactory<T>();
- }
我们看到这里创建了一个代理类工厂
然后使用代理类工厂创建了Mock对象
接着来看一下JavaProxyFactory类的实现
- public class JavaProxyFactory<T> implements IProxyFactory<T> {
- @SuppressWarnings ( "unchecked" )
- public T createProxy(Class<T> toMock, InvocationHandler handler) {
- return (T) Proxy.newProxyInstance(toMock.getClassLoader(),
- new Class[] { toMock }, handler);
- }
- }
这里使用了JDK中的java.lang.reflect.Proxy类来实现动态代理类的创建
-------------------------------------------------------
关于JDK的动态代理这里补充一个简单的例子给不太熟悉的同学
就从我们开始的Adder接口说起
补充一个实现类
现在我想实现一个DEBUG功能,就是在执行add()方法的时候
能在控制台输出一行 "1 + 2 = 3"
使用Proxy模式,我们可以这样实现
- public class AdderDebugProxy implements Adder{
- private Adder delegate;
- public AdderDebugProxy(Adder delegate){
- this .delegate = delegate;
- }
- public int add( int x, int y){
- int result = delegate.add(x, y);
- System.out.println("" + x + " + " + y + " = " + result);
- return result;
- }
- }
- public static void main(String[] args) {
- Adder adder = new AdderDebugProxy( new AdderImpl());
- adder.add(1 , 2 );
- }
程序输出
1 + 2 = 3
但是这是一个静态代理,我们的代理类必须要静态写死
如果需要在程序运行时再生成代理类的话,就要使用JDK的动态代理功能
由于无法直接定义代理类,我们需要借助一个
java.lang.reflect.InvocationHandler
来定义我们需要的代理行为
- public class DebugInvocationHandler implements InvocationHandler {
- private Adder delegate;
- public DebugInvocationHandler(Adder delegate) {
- this .delegate = delegate;
- }
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- try {
- if (method.getName().equals( "add" )){
- if ( null == args || args.length != 2 ){
- throw new IllegalArgumentException( "wrong argument length for add()" );
- }
- Integer x = (Integer)args[0 ];
- Integer y = (Integer)args[1 ];
- Integer result = delegate.add(x.intValue(), y.intValue());
- System.out.println("" + x + " + " + y + " = " + result);
- return result.intValue();
- }
- return method.invoke(delegate, args);
- } catch (InvocationTargetException e){
- throw e;
- }
- }
- }
在实际使用的时候,对于动态生成的Proxy类
调用proxy.add(int x, int y)
将会被封装成对InvocationHandler的调用
invoke(proxy, method, args)
其中method为add方法
args为封装x和y的Object数组
最后我们使用一个工厂类来创建它
- public class AdderProxyFactory {
- public static Adder createDebugProxy(Adder delegate) {
- return (Adder) Proxy.newProxyInstance(delegate.getClass()
- .getClassLoader(), delegate.getClass().getInterfaces(),
- new DebugInvocationHandler(delegate));
- }
- }
- public static void main(String[] args) {
- Adder adder = AdderProxyFactory.createDebugProxy(new AdderImpl());
- adder.add(1 , 2 );
- }
程序输出
1 + 2 = 3
-------------------------------------------------------
我们回过头来看EasyMock的源码
- return proxyFactory.createProxy(toMock, new ObjectMethodsFilter(
- toMock, new MockInvocationHandler( this ), null ));
可以看到传入了两个参数,一个被MOCK的接口
一个ObjectMethodsFilter
这个ObjectMethodsFilter正如其名
做了一个中间层,起到了过滤Object中3个方法
equals() toString() hashCode()的作用
实际起作用的是MockInvocationHandler
而传入的this参数则间接的将MocksControl的引用传给了它所创建的MOCK对象
in org.easymock.internal.MockInvocationHandler
- public final class MockInvocationHandler implements InvocationHandler, Serializable {
- private static final long serialVersionUID = -7799769066534714634L;
- private final MocksControl control;
- public MockInvocationHandler(MocksControl control) {
- this .control = control;
- }
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- try {
- if (control.getState() instanceof RecordState) {
- LastControl.reportLastControl(control);
- }
- return control.getState().invoke(
- new Invocation(proxy, method, args));
- } catch (RuntimeExceptionWrapper e) {
- throw e.getRuntimeException().fillInStackTrace();
- } catch (AssertionErrorWrapper e) {
- throw e.getAssertionError().fillInStackTrace();
- } catch (ThrowableWrapper t) {
- throw t.getThrowable().fillInStackTrace();
- }
- }
- public MocksControl getControl() {
- return control;
- }
- }
三、浅析MOCK对象的状态机制-----State模式的应用
我们直接看上面的代码,可以看到对MOCK对象的方法调用
直接被转化成了control.getState().invoke()的调用
这又是怎样的实现,我们回过头来看MocksControl的代码
- public class MocksControl implements IMocksControl, IExpectationSetters<Object>, Serializable {
- // .......
- private IMocksControlState state;
- private IMocksBehavior behavior;
- // .......
- public final void reset() {
- behavior = new MocksBehavior(type == MockType.NICE);
- behavior.checkOrder(type == MockType.STRICT);
- behavior.makeThreadSafe(false );
- state = new RecordState(behavior);
- LastControl.reportLastControl(null );
- }
- // ......
- public void replay() {
- try {
- state.replay();
- state = new ReplayState(behavior);
- LastControl.reportLastControl(null );
- } catch (RuntimeExceptionWrapper e) {
- throw (RuntimeException) e.getRuntimeException().fillInStackTrace();
- }
- }
- // ......
- public IExpectationSetters<Object> andReturn(Object value) {
- try {
- state.andReturn(value);
- return this ;
- } catch (RuntimeExceptionWrapper e) {
- throw (RuntimeException) e.getRuntimeException().fillInStackTrace();
- }
- }
可以看到这是一个State模式的应用
MocksControl中保存了一个IMocksControlState的实例对象
IMocksControlState接口有两个实现类,正是RecordState和ReplayState,定义了不同的操作
而MocksControl类使用reset()和replay()实现状态的迁移
而一些其他操作,则由MocksControl对外提供接口,交由State实现
而IMocksBehavior则是MockControl对象的数据模型
保存了RecordState中储存的调用
以供ReplayState取用
四、浅析EASYMOCK的数据模型
接着我们进入MocksBehavior看一看EasyMock的数据模型
一路找下去有很多的层次,最后找到几个核心类:
org.easymock.internal.ExpectedInvocation
org.easymock.internal.Invocation
org.easymock.IArgumentMatcher
org.easymock.internal.Result
首先我们来看Invocation类
in org.easymock.internal.Invocation
- private final Object mock;
- private transient Method method;
- private final Object[] arguments;
这个类有3个属性:MOCK对象、函数和参数列表,用于保存一次对MOCK对象的调用信息
在MockInvocationHandler中,方法调用被包含为一个Invocation类的参数传给
State对象的invoke()方法
接着来看ExpectedInvocation类
in org.easymock.internal.ExpectedInvocation
- private final Invocation invocation;
- // ......
- private final List<IArgumentMatcher> matchers;
这个类保存了一个调用信息和一系列ArgumentMatcher
这就是在RecordState中保存的调用信息
在ReplayState中MOCK对象接受方法调用
将会产生一个actual的Invocation对象
利用ExpectedInvocation类的matches()方法,EASYMOCK将匹配这个actual对象和原来记录的调用对象
in org.easymock.internal.ExpectedInvocation
- public boolean matches(Invocation actual) {
- return matchers != null ? this .invocation.getMock().equals(
- actual.getMock())
- && this .invocation.getMethod().equals(actual.getMethod())
- && matches(actual.getArguments()) : this .invocation.matches(
- actual, matcher);
- }
在这个函数中,matchers被用来比较两个调用的参数列表
默认的Matcher为org.easymock.internal.matchers.Equals
这个Matcher使用equals()方法来比较两个参数
在这个包下,EasyMock还定义了很多Matcher给使用者方便的使用
如果用户觉得不够够用的话,还可以自己来实现IArgumentMatcher
Result类实现了IAnswer接口,用来表示函数调用的返回(正常返回值或者异常抛出)
其内部有两个工厂方法分别用来创建ThrowingAnswer和ReturningAnswer
in org.easymock.internal.Result
- private IAnswer<?> value;
- private Result(IAnswer<?> value) {
- this .value = value;
- }
- public static Result createThrowResult( final Throwable throwable) {
- class ThrowingAnswer implements IAnswer<Object>, Serializable {
- private static final long serialVersionUID = -332797751209289222L;
- public Object answer() throws Throwable {
- throw throwable;
- }
- @Override
- public String toString() {
- return "Answer throwing " + throwable;
- }
- }
- return new Result( new ThrowingAnswer());
- }
- public static Result createReturnResult( final Object value) {
- class ReturningAnswer implements IAnswer<Object>, Serializable {
- private static final long serialVersionUID = 6973893913593916866L;
- public Object answer() throws Throwable {
- return value;
- }
- @Override
- public String toString() {
- return "Answer returning " + value;
- }
- }
- return new Result( new ReturningAnswer());
- }
- // .......
- public Object answer() throws Throwable {
- return value.answer();
- }
这就是在RecordState中使用andReturn()和andThrow()方法将会保存的信息
在ReplayState中,Result将会被取出,其answer()方法被调用
in org.easymock.internal.ReplayState
- private Object invokeInner(Invocation invocation) throws Throwable {
- Result result = behavior.addActual(invocation);
- LastControl.pushCurrentArguments(invocation.getArguments());
- try {
- try {
- return result.answer();
- } catch (Throwable t) {
- throw new ThrowableWrapper(t);
- }
- } finally {
- LastControl.popCurrentArguments();
- }
- }
Mock对象则会返回我们需要的值,或者抛出我们需要的异常
五、EASYMOCK Class extension的MOCK对象创建-----CGLIB动态代理
继续回顾EASYMOCK的使用
如果我们的Adder做一个小修改,现在不是接口了,是实现类或者虚基类
那么org.easymock.EasyMock.createMock()就不能使用了
因为JDK的动态代理不能生成具体类的代理
这里就需要使用org.easymock.classextension.EasyMock.createMock()来创建代理类
而这里面使用的方法就是CGLIB的Enhancer字节码增强
in org.easymock.classextension.EasyMock
- public static <T> T createMock(Class<T> toMock) {
- return createControl().createMock(toMock);
- }
- // ......
- public static IMocksControl createControl() {
- return new MocksClassControl(MocksControl.MockType.DEFAULT);
- }
而MocksClassControl是MocksControl的子类
它继承了父类的createControl方法
in org.easymock.internal.MocksControl
- public <T> T createMock(Class<T> toMock) {
- try {
- state.assertRecordState();
- IProxyFactory<T> proxyFactory = createProxyFactory(toMock);
- return proxyFactory.createProxy(toMock, new ObjectMethodsFilter(
- toMock, new MockInvocationHandler( this ), null ));
- } catch (RuntimeExceptionWrapper e) {
- throw (RuntimeException) e.getRuntimeException().fillInStackTrace();
- }
- }
但是Override了createProxyFactory()方法
in org.easymock.classextension.internal.MocksClassControl
- @Override
- protected <T> IProxyFactory<T> createProxyFactory(Class<T> toMock) {
- if (toMock.isInterface()) {
- return super .createProxyFactory(toMock);
- }
- return new ClassProxyFactory<T>();
- }
对于实际的类,它返回ClassProxyFactory
而ClassProxyFactory正是使用了CGLIB来创建代理类
这里再附一个CGLIB的简单例子,在ClassProxyFactory也能找到相类似的Proxy创建代码
--------------------------------------------------------
使用用我们的AdderImpl具体类
- public class DebugMethodIntercepter implements MethodInterceptor {
- public Object intercept(Object obj, Method method, Object[] args,
- MethodProxy proxy) throws Throwable {
- // 对proxy类的调用将会转化为对其父类的调用
- Object result = proxy.invokeSuper(obj, args);
- System.out.println("" + (Integer) args[ 0 ] + " + " + (Integer) args[ 1 ]
- + " = " + (Integer) result);
- return result;
- }
- }
- public static void main(String[] args) {
- AdderImpl adder = createDebugProxy();
- adder.add(1 , 2 );
- }
- public static AdderImpl createDebugProxy() {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(AdderImpl.class );
- enhancer.setCallback(new DebugMethodIntercepter());
- return (AdderImpl)enhancer.create();
- }
程序返回
1 + 2 = 3
转自:http://shlteater.iteye.com/blog/394191
相关推荐
Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。
星闪技术介绍
源代码
Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。
328328328328
QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。 邮箱:market@qyresearch.com
人工智能python安装环境(Miniconda3-py39-)(pycharm-community-2021.3.2.exe)
钢铁是怎样炼成的ppt模板x.pptx
封面 标题:基于物联网的智能家居系统年度总结 报告人信息:[姓名]、[职位/角色]、[所属机构/公司] 日期:[具体日期] 目录 引言 年度工作回顾 系统进展与亮点 技术创新与应用 市场反馈与用户评价 存在问题与挑战 未来展望与计划 结束语与感谢 一、引言 简要介绍智能家居系统的重要性和发展趋势 回顾本年度的工作目标和重点 二、年度工作回顾 系统建设与维护 完成的项目与里程碑 系统稳定性与可靠性提升 团队建设与培训 团队成员构成与职责 培训与技能提升活动 合作伙伴与资源整合 与供应商、合作伙伴的合作情况 资源整合与利用 三、系统进展与亮点 功能扩展与优化 新增功能介绍与效果评估 现有功能的优化与改进 用户体验提升 界面设计与交互优化 用户反馈与改进措施 四、技术创新与应用 物联网技术的应用 传感器与通信技术的升级 大数据分析与应用 智能家居的智能化管理 自动化控制与节能策略 安全防护与预警系统 五、市场反馈与用户评价 市场反馈分析 市场需求与竞争态势 市场占有率与增长趋势 用户评价总结 用户满意度调查结果
LabVIEW专栏一、编写单独vi
SQL/数据库查询语言
flutter 常用插件整理附加使用实例
IMG_20240327_093721.jpg
列车车厢重排问题是一个著名的组合优化问题,也被称为火车车厢重排问题(Railway Carriage Shunting Problem),它的目标是通过尽可能少的操作将一列乱序的车厢重新排列成有序的顺序。 在这个问题中,我们有一列由1到n号标记的乱序车厢。初始时,所有的车厢都停在一条没有分叉的轨道上。现在我们要进行一系列操作来将车厢按照升序排列。每个操作可以将车厢从一段轨道移到另一端轨道的任意位置。而这些操作的目标是,经过一定的操作后,所有车厢按照升序排列。 这是一个经典的组合优化问题,可以使用多种算法来解决。其中一种常见的解法是使用贪心算法。
Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。
简历-求职简历-word-文件-简历模版免费分享-应届生-高颜值简历模版-个人简历模版-简约大气-大学生在校生-求职-实习 简历是展示个人经历、技能和能力的重要文档,以下是一个常见的简历格式和内容模板,供您参考: 简历格式: 头部信息:包括姓名、联系方式(电话号码、电子邮件等)、地址等个人基本信息。 求职目标(可选):简短描述您的求职意向和目标。 教育背景:列出您的教育经历,包括学校名称、所学专业、就读时间等。 工作经验:按时间顺序列出您的工作经历,包括公司名称、职位、工作时间、工作职责和成就等。 技能和能力:列出您的专业技能、语言能力、计算机技能等与职位相关的能力。 实习经验/项目经验(可选):如果您有相关实习或项目经验,可以列出相关信息。 获奖和荣誉(可选):列出您在学术、工作或其他领域获得的奖项和荣誉。 自我评价(可选):简要描述您的个人特点、能力和职业目标。 兴趣爱好(可选):列出您的兴趣爱好,展示您的多样性和个人素质。 参考人(可选):如果您有可提供推荐的人员,可以在简历中提供其联系信息。 简历内容模板: 姓名: 联系方式: 地址: 求职目标: (简短描述您的求职意
Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。
智能床新品618营销推广项目方案ss.pptx
asp代码《软件工程》精品课程教学网站的设计与实现 ASP+SQL Sever2000本资源系百度网盘分享地址