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-buddyjar版本时,这个问题通常会发生。错误如下所示:

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表达式为模拟静态对象提供了更优雅的解决方案。

大家在使用的过程中,需要结合自身项目的实际情况,多方位调试,以找准更优的融合方法。

7. 引用

  1. 如何mock静态方法
  2. mock初始化错误

标签: 技术, Java

添加新评论