Easymock的基本思路就是在录制状态时将录制结果保存到IMocksBehavior中,当变为回放状态时从IMocksBehavior中取出录制结果。IMocksBehavior保存及取出预设值的类图如下
Range保存设定的调用次数;Result保存设定的值;Results根据调用次数的不同返回不同的值;Invocation 保存mock对象、函数、以及默认参数;IArgumentMatcher的实现类封装了函数参数的匹配方法;ExpectedInvocation判断mock对象调用的函数与录制函数是否匹配;ExpectedInvocationAndResults保存返回结果与调用函数间的对应关系;UnorderedBehavior添加预设值以及根据调用来返回相应的值;MocksBehavior根据mock的三种类型,确定换回值还是抛出异常。
easymock的录制过程即设定mock对象的预设值
EasyMock.expect(mock.oneArg(EasyMock.anyObject(String.class))).andReturn("2").times(1,2);
或
//mock对象调用的方法匹配的参数
EasyMock.anyObject(String.class);
mock1.oneArg(null);
final IExpectationSetters exceptionSetters =EasyMock.expectLastCall();
exceptionSetters.andReturn("2");
exceptionSetters.times(1,2);
以上两种调用方法的结果相同,详细步骤为:
1、先将调用方法要参数匹配的类压入栈中
参数匹配类都在org.easymock.internal.matchers包中,类中封装的是参数的匹配算法,时序图如下:
LastControl类:
public static void reportMatcher(final IArgumentMatchermatcher) {
Stack<IArgumentMatcher>stack = threadToArgumentMatcherStack.get();
if (stack== null) {
stack =new Stack<IArgumentMatcher>();
threadToArgumentMatcherStack.set(stack);
}
stack.push(matcher);
}
压入栈中的个数必须与要录制的方法的参数相匹配的。
当将and、or参数匹配类压入栈中的时候会将栈顶的前两个元素合并为一个,因此栈中必须有两个元素。代码:
LastControl类:
public static void reportAnd(final int count) {
finalStack<IArgumentMatcher> stack = threadToArgumentMatcherStack.get();
assertState(stack != null, NO_MATCHERS_FOUND);
stack.push(new And(popLastArgumentMatchers(count)));
}
EasyMock调用reportAnd方法时count=2.
2、调用要录制的方法
mock1.oneArg(null);
时序图:
以上两步主要是生成ExpectedInvocation对象,因为easymock返回预期结果时不仅调用函数相等而且要查看函数的参数是否匹配。
当1步没有时,将会在第2步调用要录制的方法生成ExpectedInvocation对象时,匹配对象设置为Equals类,当回放时调用函数的
参数对象必须与录制时的参数对象相等才会匹配。
3、设定预期结果
时序图如下:
3.1获取预期结果设置对象exceptionSetters
获取最后一次调用代理方法的mock对象对应的mockControl对象,而这个mock对象正是第2步中被MockInvocationHandler设置到LastControl中的mockControl对象。
3.2设定返回结果
exceptionSetters.andReturn("2")
为recordState对象属性lastResult赋值,但暂时不会加入到结果记录对象behavior当中。
RecordState类andReturn方法
public void andReturn(Object value) {
//判断lastInvocation方法是否存在,没有则报错,因为预设的值必须对应invocation对象
requireMethodCall("return value");
//将值转换为相应的值
value =convertNumberClassIfNeccessary(value);
//判断预设的值类型与方法要求返还的类型是否相对应
requireAssignable(value);
//如果最后一次预设的值还没有保存,则保存
if(lastResult != null) {
times(MocksControl.ONCE);
}
lastResult= Result.createReturnResult(value);
}
3.3设定调用的次数
exceptionSetters.times(1,2),设定预设返回结果返回的次数,并将预设的结果保存到behavior中。
RecordState类中的times方法
public void times(final Range range) {
requireMethodCall("times");
requireLastResultOrVoidMethod();
behavior.addExpected(lastInvocation, lastResult != null ? lastResult : Result
.createReturnResult(null), range);
lastInvocationUsed = true;
lastResult = null;
}