模拟不适用于 ExecutorService.submit()

问题描述 投票:0回答:1

使用执行器服务时,模拟不起作用。以下是代码。

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

public class DemoClass {
    public void demoMethod() {
        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(() -> processList(transaction)));

        // this works well and print "mockedName"
        // processList(new MyUser("withoutExecutor"));
        executor.shutdown();
    }

    public void processList(MyUser user){
        String name = MyEmployee.getEmployeeName();
        System.out.println(name);
    }
}
***********************************************
public class MyEmployee {
    public static String getEmployeeName() {
        return "testEmpName";
    }
}
***********************************************
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 static org.mockito.Mockito.mockStatic;

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class DemoClassTest {
    private DemoClass demoClass;
    private MockedStatic<MyEmployee> myEmployeeMockedStatic;
    @Before
    public void before() throws Exception {
        demoClass = new DemoClass();
        myEmployeeMockedStatic = mockStatic(MyEmployee.class);
    }

    @Test
    public void myTest() {
        // given
        myEmployeeMockedStatic.when(MyEmployee::getEmployeeName).thenReturn("mockedName");
        // when
        demoClass.demoMethod();
    }
    @After
    public void after() {
        myEmployeeMockedStatic.close();
    }
}

它正在打印“testEmpName”,期望的结果是“mockedName” 在提交方法的调用中调用的任何静态方法都不会被模拟。这可能是因为池中的线程,但在这种情况下我们如何模拟静态方法?

研究结果:

  1. 在 executor.submit() 中没有任何模拟可以工作

  2. 原因是 - 模拟无法在不同的线程上工作。

  3. 当我们执行 executor.submit() 时 - 它在不同的线程上启动进程 - 这就是模拟不起作用的原因

  4. 到目前为止,我看到很少有帖子表明模拟不支持这一点。 https://github.com/mockito/mockito/issues/2142

  5. 但是我知道我们可以找到一些解决方法 - 就像我们可以模拟执行器并在该模拟中传递我们所需的模拟。还是不知道该怎么做。

  6. 即使有人对上面的代码进行了解码。参考:https://github.com/mockito/mockito/issues/2142

Executor executor = you executor....;
doAnswer(new Answer<Object>() {
    public Object answer(InvocationOnMock invocation)
        throws Exception {
        Object[] args = invocation.getArguments();
        Runnable runnable = (Runnable)args[0];
        try (MockedConstruction<SomeClass> mockedConstructor = Mockito.mockConstruction(SomeClass.class);
              MockedStatic<SomeStaticUtil> mockedUtil = mockStatic(SomeStaticUtil.class))
        {
            ....do mocks things
            runnable.run();
        }
        return null;
    }
}).when(executor).execute(any());
java multithreading junit mockito executor
1个回答
0
投票

一种解决方法是传入不同的 ExecutorService 或 Executor,它们在同一线程上执行任务。

请注意,您的代码需要进行一些重构 - 您需要将 Executor 传递给您的 DemoClass,这将改变它的运行时行为。

在你的情况下,Executor就足够了,所以你可以使用简单的实现:

Executor executor = Runnable::run;

更多选项,请参阅是否有使用当前线程的ExecutorService?

整个实施:

演示课程

public class DemoClass {

    private final Executor executor;

    public DemoClass() {
        this(Executors.newFixedThreadPool(2));
    }

    public DemoClass(Executor executor) {
        this.executor = executor;
    }

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

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

演示类测试

public class DemoClassTest {
    private final Executor executor = Runnable::run;
    private final DemoClass demoClass = new DemoClass(executor);

    @Test
    public void myTest() {
        try(MockedStatic<MyUser> mUserMockedStatic = mockStatic(MyUser.class)) {
            // given
            mUserMockedStatic.when(MyUser::getEmployeeName).thenReturn("mockedName");
            // when
            demoClass.demoMethod();
        }
    }
}

备注:

  • 在使用它的方法中创建 ExecutorService 并立即关闭它不像传入一个 ExecutorService 那样常见 - 至少在我到目前为止看到的代码中是这样。
  • static
    getEmployeeName
    没有什么意义 - 但我认为这只是一个例子
  • 按照您的问题中的建议,模拟 ExecutorService 在同一线程中工作没有什么意义 - 同一线程 Executors 也可以正常工作。
© www.soinside.com 2019 - 2024. All rights reserved.