无法模拟 Executors.newSingleThreadExecutor() 并调用真正的方法

Cannot mock Executors.newSingleThreadExecutor() and the real method is called

我正在尝试为此方法编写单元测试:

public class ClassToTest{
....
    public void execute() {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(this::launch);
    }
....
}

并且我尝试模拟 Executors.newSingleThreadExecutor() 使 executorService 与我的测试在同一线程上运行,以便我的断言发生在 executorService 执行之后而不是之前:

@RunWith(PowerMockRunner.class)
@PrepareForTest({
        AnOtherClass.class,
        Executors.class
})
public class TestClass {

    @Before
    public void setUp() throws Exception {
        PowerMockito.mockStatic(Executors.class);
        ExecutorService executorService = mock(ExecutorService.class);
        implementAsDirectExecutor(executorService);
        PowerMockito.when(Executors.newSingleThreadExecutor()).thenReturn(executorService);
    }

    private void implementAsDirectExecutor(ExecutorService executor) {
        doAnswer(invocation -> {
            Object[] args = invocation.getArguments();
            Runnable runnable = (Runnable)args[0];
            runnable.run();
            return null;
        }).when(executor).submit(any(Runnable.class));
    }
    
    @Test
    public void testMethod() {
       ....
       classToTest.execute();
       .... //assert something
    }
}

问题:

即使 PowerMockito.when(Executors.newSingleThreadExecutor()).thenReturn(executorService); 调用了真正的方法,但我没有得到模拟的 ExecutorService。

解决方案 1

这个问题也可以通过将 ClassToTest 添加到 @PrepareForTest 来解决。

@PrepareForTest({
        AnOtherClass.class,
        Executors.class,
        ClassToTest.class
})

解决方案 2

但我更喜欢@Andy的解决方案,它更干净:

我在 ClassToTest 的构造函数中传递了我的 ExecutorService,就这样,我摆脱了 PowerMock:

public class ClassToTest{
    ....
    public ClassToTest(ExecutorService executorService) {
        ....
    }
....
    public void execute() {
        executorService.submit(this::launch);
    }
....
}

另一种思考方式是将 ExecutorService 注入 test-class 以进行测试:

public class ClassToTest {
   private ExecutorService testExecutor;
   ...
   public void execute() {
       ExecutorService executor;
       if (testExecutor == null) {
           executor = Executors.newSingleThreadExecutor();
       } else {
           executor = testExecutor;
       }
       executor.submit(this::launch);
    }
    ...
    public void setTestExecutor(ExecutorService testExecutor) {
        this.testExecutor = testExecutor;
    }
}

然后您可以使用 PowerMockito 的更多标准接口模拟,而不是覆盖静态 classes。