You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ExecutorService.submit()场景下Mockito静态方法Mock失效问题求助

解决ExecutorService中Mockito静态方法Mock不生效的问题

问题本质

Mockito的MockedStatic是线程局部绑定的,只有创建该mock的线程能识别到静态方法的mock逻辑。线程池的工作线程属于独立线程,无法继承测试线程的mock上下文,因此静态方法会执行原逻辑而非mock后的逻辑。


方案1:重构代码注入ExecutorService(推荐)

将业务代码中的ExecutorService改为可注入的方式,测试时传入同步执行的Executor,让任务在测试线程中运行,复用已有的静态mock上下文。

修改业务代码(DemoClass)

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;

public class DemoClass {
    private final ExecutorService executor;

    // 生产环境默认构造方法
    public DemoClass() {
        this(java.util.concurrent.Executors.newFixedThreadPool(2));
    }

    // 测试专用构造方法,注入自定义Executor
    public DemoClass(ExecutorService executor) {
        this.executor = executor;
    }

    public void demoMethod() {
        List<MyUser> myUserList = new ArrayList<>();
        myUserList.add(new MyUser("ABC"));
        myUserList.add(new MyUser("XYZ"));
        
        myUserList.forEach(transaction -> executor.submit(() -> processList(transaction)));
        executor.shutdown();
    }

    public void processList(MyUser user){
        String name = MyEmployee.getEmployeeName();
        System.out.println(name);
    }
}

修改测试代码

使用同步Executor(可借助Guava的MoreExecutors.directExecutor(),或自定义实现),确保任务在测试线程执行,同时等待任务完成:

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static org.mockito.Mockito.mockStatic;

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class DemoClassTest {
    private DemoClass demoClass;
    private MockedStatic<MyEmployee> myEmployeeMockedStatic;
    // 自定义同步Executor,任务直接在当前线程执行
    private final ExecutorService syncExecutor = Executors.newSingleThreadExecutor(r -> {
        Thread thread = new Thread(r);
        thread.setContextClassLoader(Thread.currentThread().getContextClassLoader());
        return thread;
    });

    @Before
    public void before() {
        demoClass = new DemoClass(syncExecutor);
        myEmployeeMockedStatic = mockStatic(MyEmployee.class);
    }

    @Test
    public void myTest() throws InterruptedException {
        myEmployeeMockedStatic.when(MyEmployee::getEmployeeName).thenReturn("mockedName");
        
        demoClass.demoMethod();
        
        // 等待所有任务执行完成
        syncExecutor.shutdown();
        while (!syncExecutor.awaitTermination(1, TimeUnit.SECONDS)) {
            // 循环等待直到线程池终止
        }
    }

    @After
    public void after() {
        myEmployeeMockedStatic.close();
    }
}

方案2:使用PowerMock实现跨线程静态Mock

PowerMock基于字节码修改实现静态方法Mock,不依赖线程局部变量,因此可跨线程生效。无需修改业务代码,但需引入PowerMock依赖。

添加Maven依赖

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

修改测试代码

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.concurrent.TimeUnit;

@RunWith(PowerMockRunner.class)
@PrepareForTest(MyEmployee.class)
public class DemoClassTest {
    private final DemoClass demoClass = new DemoClass();

    @Test
    public void myTest() throws InterruptedException {
        // Mock静态方法,跨线程生效
        PowerMockito.mockStatic(MyEmployee.class);
        PowerMockito.when(MyEmployee.getEmployeeName()).thenReturn("mockedName");

        demoClass.demoMethod();
        
        // 等待线程池任务执行完成
        TimeUnit.SECONDS.sleep(1);
    }
}

方案3:ThreadLocal传递Mock上下文(不推荐)

手动将测试线程的MockedStatic上下文传递给工作线程,但会侵入业务代码,破坏测试与业务的分离:

// 修改DemoClass的demoMethod,需传入MockedStatic对象
public void demoMethod(MockedStatic<MyEmployee> staticMock) {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    List<MyUser> myUserList = new ArrayList<>();
    myUserList.add(new MyUser("ABC"));
    myUserList.add(new MyUser("XYZ"));

    myUserList.forEach(transaction -> executor.submit(() -> {
        // 在工作线程中绑定mock上下文
        try (MockedStatic<MyEmployee> ignored = staticMock) {
            processList(transaction);
        }
    }));

    executor.shutdown();
}

总结

优先选择方案1,它遵循依赖注入原则,提升代码可测试性与维护性,无需依赖额外测试框架。方案2适合无法重构业务代码的场景,但需注意PowerMock的兼容性问题。方案3侵入性强,仅作为临时 workaround 使用。

内容的提问来源于stack exchange,提问作者Feku279

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 05:22:48