4

I have a code like this for which I would like to write unit test.

public class TestClass {

private final Executor executor;
private final Handler handler;

TestClass(Executor executor, Handler handler) {
    this.executor = executor;
    this.handler = handler;
}

void doSomething(String param1) {
    executor.execute(new Runnable() {
        @Override
        public void run() {
            //do something
            handler.callHandler();
        }
    });
}
}

How can I use Mockito / Powermockito to verify if the callHandler() method is invoked.

Ankit
  • 115
  • 1
  • 3
  • 12
  • 1
    I actually don't like the method `doSomething`. The problem you have here is that this method does two thing: create the Runnable and let it be executed by the executor. I would refactor that method to receive the already build Runnable, so it only calls `executor.execute(runnable);` and then it would be much easier to test it. – Tom Jun 20 '17 at 11:14

2 Answers2

8

Pass a mock Handler to the constructor of TestClass.

Then use Mockito.verify() to assert that callHandler() method was called.

Involving concurrency

You can stub an answer that counts down on a CountDownLatch to make the test wait for the handler to be hit. Waiting will involve setting a reasonable timeout, which can be tricky, you don't want it too high, or failure will make the test run much longer, and not too low so that you don't get false positives.

Handler handler = mock(Handler.class);
CountDownLatch finished = new CountDownLatch(1);

doAnswer(invocation -> {
    finished.countDown();
    return null;
}).when(handler).callHandler();

TestClass testClass = new TestClass(executor, handler);

testClass.doSomething("thisThing");

boolean ended = finished.await(10, TimeUnit.SECONDS);

assertThat(ended).isTrue();

verify(handler).callHandler();

Bypassing concurrency

If you're only trying to determine whether the handler is invoked you can use an Executor that executes on the same thread. This will make for a more stable test.

Handler handler = mock(Handler.class);
Executor executor = new Executor() {
    @Override
    public void execute(Runnable command) {
        command.run();
    }
};

TestClass testClass = new TestClass(executor, handler);

testClass.doSomething("thisThing");

verify(handler).callHandler();
bowmore
  • 10,842
  • 1
  • 35
  • 43
  • And you can assure that the executor already ran the `Runnable` when the test method reaches `verify(handler).callHandler();`? – Tom Jun 20 '17 at 11:12
  • Ah, this is a concurrency problem, not a Mockito problem? – bowmore Jun 20 '17 at 11:16
  • 1
    [This](https://stackoverflow.com/questions/6581188/is-there-an-executorservice-that-uses-the-current-thread) should help with the concurrency problem. – Andrew S Jun 20 '17 at 11:18
  • Well, at least both, I would say. The executor can be tricky, because we can't know for sure, when it actually ran the process :( (well I don't know any method to know that) – Tom Jun 20 '17 at 11:19
  • 1
    If the test is just trying to assert that the handler is invoked its probably easiest to supply an Executor that executes on the same thread. – bowmore Jun 20 '17 at 11:21
0

Another way you can handle the concurrency issue is to mock the Executor to "do nothing" when called and use an ArgumentCaptor in your test to capture the Runnable it would have invoked. Once you have the Runnable you can manually invoke it in the same thread as your test.

Here's an example:

@Mock
private Executor executor;
@Mock
private Handler handler;

@Before
public void setup() {
    openMocks(this);

    doNothing().when(executor).execute(any());
}

@Test
public void runTest() {
    TestClass testClass = new TestClass(executor, handler);
    testClass.doSomething("the thing");

    ArgumentCaptor<Runnable> runnable = ArgumentCaptor.forClass(Runnable.class);
    verify(executor).execute(runnable.capture());
    Runnable capturedRunnable = runnable.getValue();
    capturedRunnable.run();

    verify(handler).callHandler();
}

StoriKnow
  • 5,738
  • 6
  • 37
  • 46