模拟一个自动装配的 ExecutorService

Mock an Autowired ExecutorService

摘要:

我有一个 Spring @Component,它使用自动装配的 ExecutorService 作为工作池。我正在使用 JUnit 和 Mockito 来测试组件的功能,我需要模拟该执行程序服务。这对于其他自动装配的成员来说是微不足道的——一个通用的助手,例如一个 DAO 层很容易被模拟,但我需要一个 真实的 执行器服务。

代码:

@RunWith(MockitoJUnitRunner.class)
public class MadeUpClassNameTest{

  @Mock
  private ExecutorService executor;

  @Before
  public void initExecutor() throws Exception{
      executor = Executors.newFixedThreadPool(2);
  }

  @InjectMocks
  private ASDF componentBeingAutowired;
...

单独这样做是行不通的,invokeAll()的结果总是一个空列表。

尝试更明确地模拟执行程序方法也不起作用...

@Test
public void myTestMethod(){
    when(executor.invokeAll(anyCollection()))
        .thenCallRealMethod();
    ...
}

我收到措辞隐晦的异常:

You cannot use argument matchers outside of verification or stubbing.

(我以为这是打桩?)

可以 提供一个 thenReturn(Answer<>) 方法,但我想确保代码确实与执行程序一起工作,代码的一部分是致力于绘制期货结果。

问题 如何提供真实的(或功能上可用的模拟)执行程序服务?或者,我在测试这个组件时遇到的困难是否表明这是一个需要重构的糟糕设计,或者可能是一个糟糕的测试场景?

注释 我想强调我的问题不是设置 Mockito 或 Junit。其他模拟和测试工作正常。我的问题仅针对上面的特定模拟。

使用:Junit 4.12,Mockito 1.10.19,Hamcrest 1.3

我认为注入 Mock 后会运行以下代码。

@Before
public void initExecutor() throws Exception{
  executor = Executors.newFixedThreadPool(2);
}

这会导致设置 executor 的本地副本,而不是注入的副本。

我建议在您的 componentBeingAutowired 中使用 constructor injection 并在您的单元测试中创建一个新的并排除 Spring 依赖项。您的测试可能如下所示:

public class MadeUpClassNameTest {
    private ExecutorService executor;

    @Before
    public void initExecutor() throws Exception {
        executor = Executors.newFixedThreadPool(2);
    }

    @Test
    public void test() {
        ASDF componentBeingTested = new ASDF(executor);
        ... do tests
    }
}

您可以使用@Spy 注释。

@RunWith(MockitoJUnitRunner.class)
public class MadeUpClassNameTest{

    @Spy
    private final ExecutorService executor = Executors.newFixedThreadPool(2);

    ....

}

您可以使用@Spy 注释。

@RunWith(MockitoJUnitRunner.class)
public class MadeUpClassNameTest{
@Spy
private final ExecutorService executor = Executors.newFixedThreadPool(2);

@Test
...
}

另一种方法是使用ReflectionTestUtils注入执行器

@Before
public void initExecutor() {
  ReflectionTestUtils.setField(componentBeingAutowired, "executor", Executors.newFixedThreadPool(2);
}