使用Mockito模拟静态方法
1. 概述
在某项目单元测试编码过程中,当前覆盖率大约在50%
左右。一直无法得到有效提升的原因之一便是无法针对HttpUtil.postByJson
这类方法进行Mock。
经过代码调查,将此类问题抽象为如何Mock静态方法?本教程将针对这一问题进行说明——如何针对静态方法的模拟进行说明。
2. 一个简单的静态类
/**
* @author taliove
* @author taliove 2019/4/10 09:42 新增针对不同的编码格式返回
*/
public class HttpUtil {
private HttpUtil() {}
/**
* 使用JSON格式发送POST请求
*
* @param url 请求地址
* @param param 请求参数
* @return
*/
public static String postByJson(String url, Object param) throws IOException {
return "ok";
}
/**
* 无参静态方法示例
*
* @return
*/
public static String name() {
return "test";
}
}
为了方便于演示,此处封装了一个简单的返回"ok"
的方法:postByJson
。
3. 依赖关系
当前阶段,该平台使用的mockito
的版本号为2.x.x
。该版本暂不支持静态方法的模拟。此处使用的是3.8.0
版本。
dependencies {
// <https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy>
implementation group: 'net.bytebuddy', name: 'byte-buddy', version: '1.10.20'
testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.8.0'
testImplementation group: 'org.mockito', name: 'mockito-inline', version: '3.8.0'
// <https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy-agent>
testImplementation group: 'net.bytebuddy', name: 'byte-buddy-agent', version: '1.9.3'
// <https://mvnrepository.com/artifact/org.objenesis/objenesis>
testImplementation group: 'org.objenesis', name: 'objenesis', version: '2.6'
}
mockito
针对静态方法的模拟需要依赖包mockito-inline
。
mokito
核心依赖于一个名为byte-buddy
的库,当mocito找不到匹配的byte-buddy
jar版本时,这个问题通常会发生。错误如下所示:
java.lang.IllegalStateException: Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null)
解决办法是查询MVN库,查看该mokito-core的编译依赖项,并将其引入即可。该查询地址的:https://mvnrepository.com/artifact/org.mockito/mockito-core/3.8.0。本例中,已将所有需要依赖的对象纳入。
4. 测试带参静态方法
@Test
public void twoArgsTest() {
Map<String, String> param = new HashMap<>();
param.put("account", "test");
try {
MockedStatic<HttpUtil> httpUtil = Mockito.mockStatic(HttpUtil.class);
httpUtil.when(() -> HttpUtil.postByJson("<http://xx/login/login_cookie>", param)).thenReturn("bar");
String resultStr = HttpUtil.postByJson("<http://xx/login/login_cookie>", param);
assertSame("bar", resultStr);
} catch (IOException e) {
e.printStackTrace();
}
}
从Mockito 3.4.0
开始,可以使用Mockito.mockStatic(Class <T> classToMock)
方法来模拟对静态方法调用的调用。此方法为我们的类型返回一个MockedStatic对象,这是一个有范围的模拟对象。
因此,在上面的单元测试中,httpUtil
变量表示具有线程局部显式作用域的模拟。
重要的是要注意,作用域模拟必须由激活该模拟的实体关闭。这就是为什么我们在try-with-resources构造中定义模拟程序的原因,以便当我们完成作用域块时自动关闭模拟程序。
5. 测试无参静态方法
@Test
public void noArgsTest() {
try (MockedStatic<HttpUtil> httpUtil = Mockito.mockStatic(HttpUtil.class)) {
httpUtil.when(HttpUtil::name).thenReturn("test");
assertSame("test", HttpUtil.name());
}
assertSame("tangf", HttpUtil.name());
}
6. 小结
在本篇短文中,举了两个示例来使用Mockito模拟静态方法。Mockito通过Lambda表达式为模拟静态对象提供了更优雅的解决方案。
大家在使用的过程中,需要结合自身项目的实际情况,多方位调试,以找准更优的融合方法。