这里举一个稍微复杂一点的例子
前段时间在看mybatis3.5.1的源码,源码本身所带的单元测试用到了mockito框架
一、问题说明
我看到MapperMethod类中有个方法叫convertToArray(私有方法),想要断点调试下运行过程,但是我不想改源码;另外我也想顺便多用用mockito框架
看到这个方法,
1. 我会想到需要传递一个list。这个好办
2. 该方法不是静态方法,需要先new一个MapperMethod。
该类只有一个带参构造方法
public MapperMethod(Class> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
这几个参数传进去又会遇到问题,因为SqlCommand和Method拿到传的参数后做了一大堆处理;想要构造出这几个参数并不是一件容易的事
3. 该方法不是私有方法,需要通过反射才能调用。这个不难
我只是为了测试convertToArray方法,顺便还得看一大堆逻辑;想到这里,可能还是会想,我还不如把这一小段代码稍微改改,贴到一段新的地方,然后自己再写个简单的测试。假如非得要实现呢?过程见下文
二、代码实现
我在运行mybatis中的单元测试方法时,发现mockito可以模拟类的返回结果。所以在这里正好可以派上用场,代码如下:
@Test
public void convertToArray() throws Exception {
List list = new ArrayList(){{
add("aa");
add("bb");
add("cc");
}};
// 1.模拟对象行为
// https://yanbin.blog/mockito-modify-private-field/
MapperMethod mockMapperMethod = mock(MapperMethod.class);
Field apiField = MapperMethod.class.getDeclaredField("method");
MapperMethod.MethodSignature mockMethodSignature = mock(MapperMethod.MethodSignature.class);
FieldSetter.setField(mockMapperMethod, apiField, mockMethodSignature);
/* 这种写法有问题
参考:https://stackoverflow.com/questions/15942880/mocking-a-method-that-return-generics-with-wildcard-using-mockito
when(mockMethodSignature.getReturnType().getComponentType())
.thenReturn(new String[]{}.getClass());*/
Mockito.>when(mockMethodSignature.getReturnType()).thenReturn(new String[]{}.getClass());
// 2.获取convertToArray方法
Method method = MapperMethod.class.getDeclaredMethod("convertToArray", List.class);
method.setAccessible(true);
Object result = method.invoke(mockMapperMethod, list);
System.out.println(result);
}
简单说下,基本和开始的分析差不多:
1. 先准备好参数List
2. 【模拟对象行为】
A. new一个MapperMethod,这时需要设置好method属性(因为convertToArray中会用到)
B. 模拟出刚刚设置过的method属性的行为,即调用method属性的getReturnType()方法时,让其方法能返回String数组对应的class
3. 【获取convertToArray方法】
A. 使用getDeclaredMethod获得方法对象,设置为可访问
B. 将先前准备好的list传入,然后调用
假如要测试List
@Test
public void convertToArray1() throws Exception {
List list = new ArrayList(){{
add(11);
add(22);
add(33);
}};
// 模拟对象行为
MapperMethod mockMapperMethod = mock(MapperMethod.class);
Field apiField = MapperMethod.class.getDeclaredField("method");
MapperMethod.MethodSignature mockMethodSignature = mock(MapperMethod.MethodSignature.class);
FieldSetter.setField(mockMapperMethod, apiField, mockMethodSignature);
Mockito.>when(mockMethodSignature.getReturnType()).thenReturn(new int[]{}.getClass());
// 获取convertToArray方法
Method method = MapperMethod.class.getDeclaredMethod("convertToArray", List.class);
method.setAccessible(true);
Object result = method.invoke(mockMapperMethod, list);
System.out.println(result);
}
参考链接:
https://yanbin.blog/mockito-modify-private-field/
https://stackoverflow.com/questions/15942880/mocking-a-method-that-return-generics-with-wildcard-using-mockito