使用mockito框架来测试私有方法

这里举一个稍微复杂一点的例子

前段时间在看mybatis3.5.1的源码,源码本身所带的单元测试用到了mockito框架

一、问题说明

我看到MapperMethod类中有个方法叫convertToArray(私有方法),想要断点调试下运行过程,但是我不想改源码;另外我也想顺便多用用mockito框架

 

使用mockito框架来测试私有方法_第1张图片

看到这个方法,

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

 

你可能感兴趣的:(java,单元测试,mybatis)