Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock an Autowired ExecutorService

Abstract:

I have a Spring @Component that uses an autowired ExecutorService as a work pool. I'm using JUnit and Mockito to test the functionality of the component and I need to mock that Executor Service. This has been trivial for other autowired members - a generic helper, and a DAO layer for instance are easily mocked, but I need a real Executor Service.

Code:

@RunWith(MockitoJUnitRunner.class)
public class MadeUpClassNameTest{

  @Mock
  private ExecutorService executor;

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

  @InjectMocks
  private ASDF componentBeingAutowired;
...

This alone doesn't work, the results of invokeAll() is always an empty list.

Attempting to more explicitly mock the executor method also doesn't work...

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

I get the cryptically worded exception:

You cannot use argument matchers outside of verification or stubbing.

(I thought this was a stubbing ?)

I could provide a thenReturn(Answer<>) method, but I'd like to make sure that the code actually works with an executor, a fair bit of the code is devoted to mapping the results of Futures.

Problem How do I provide a real (or functionally usable mock) Executor Service ? Alternatively, is my difficulty in testing this component a sign that this is a poor design in need of refactoring, or possibly a bad test scenario ?

Notes I want to stress that my problem is NOT getting Mockito or Junit set up. Other mocks and tests work correctly. My problem is specific only to the particular mock above.

Using: Junit 4.12, Mockito 1.10.19, Hamcrest 1.3

like image 734
JHarnach Avatar asked Dec 04 '15 20:12

JHarnach


2 Answers

I think the following code runs after the Mock is injected.

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

This causes your local copy of executor to be set, but not the one that is injected.

I would recommend using constructor injection in on your componentBeingAutowired and create a new one in your unit test and exclude Spring dependencies. Your test could then look like something below:

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
    }
}
like image 179
LucasP Avatar answered Nov 10 '22 17:11

LucasP


Another way is to use ReflectionTestUtils to inject the executor

@Before
public void initExecutor() {
  ReflectionTestUtils.setField(componentBeingAutowired, "executor", Executors.newFixedThreadPool(2);
}
like image 26
was_777 Avatar answered Nov 10 '22 18:11

was_777