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

Viewed 334

I'm trying to write unit tests for this method:

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

and I tried to mock Executors.newSingleThreadExecutor() to have an executorService that runs on the same thread as my tests, so that my assertions happens after the executions of the executorService and not before:

@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
    }
}

Problem :

Even with PowerMockito.when(Executors.newSingleThreadExecutor()).thenReturn(executorService); the real method is called, and I don't get the mocked ExecutorService.

2 Answers

Solution 1

This issue could be resolved by adding the ClassToTest to the @PrepareForTest also.

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

Solution 2

But I prefer the solution of @Andy, it's more clean:

I passed my ExecutorService in the constructor of ClassToTest, and like that, I get rid of PowerMock:

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

A different way to think about this is to inject the ExecutorService into the test-class for testing purposes:

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;
    }
}

Then you can use more standard interface mocking with PowerMockito as opposed to overriding the static classes.

Related