使用执行器服务时,模拟不起作用。以下是代码。
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” 在提交方法的调用中调用的任何静态方法都不会被模拟。这可能是因为池中的线程,但在这种情况下我们如何模拟静态方法?
研究结果:
在 executor.submit() 中没有任何模拟可以工作
原因是 - 模拟无法在不同的线程上工作。
当我们执行 executor.submit() 时 - 它在不同的线程上启动进程 - 这就是模拟不起作用的原因
到目前为止,我看到很少有帖子表明模拟不支持这一点。 https://github.com/mockito/mockito/issues/2142
但是我知道我们可以找到一些解决方法 - 就像我们可以模拟执行器并在该模拟中传递我们所需的模拟。还是不知道该怎么做。
即使有人对上面的代码进行了解码。参考: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());
一种解决方法是传入不同的 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();
}
}
}
备注:
getEmployeeName
没有什么意义 - 但我认为这只是一个例子