Workaround to Multi Threaded Testing
Join the DZone community and get the full member experience.
Join For FreeSince it has been introduced in JDK 1.5, I have loved the Executor abstraction over multi threaded execution. Basically you define tasks, implementing Runnable or Callable interfaces, and you submit those tasks to an Executor
implementation. It's the Executor who knows how the tasks must be processed:
scheduled at a certain time, enqueued in a separate single thread or
using a thread pool. Different instances of Executor can be obtained through the Executors class.
In this way, the logic of your program is not dependant on how the multi
threading needs to be implemented: you can think of tasks and
executors, and you can choose, at a later time, how those tasks need to
be processed. It's also possible to discover the status of a task:
by taking a look at the Future.
Another advantage that I like is the fact that you can remove the issue of the multi threading during the tests. Testing multithreaded code is quite hard, because at the time you want to verify your assertions, the parallel threads may not yet be ready, so you have to play with the sleep(), join(), wait() and notify() methods, producing sometimes unreliable tests.
So, wouldn't it be wonderful if, just for testing, you could remove the
complexity of the background execution? After all, we know that
Executors work beautifully and we don't need actually to verify them.
In fact, as the Javadocs say
However, the Executor interface does not strictly require that execution be asynchronous. In the simplest case, an executor can run the submitted task immediately in the caller's thread:
class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
Following the DIP principle, you can pass the above DirectExecutor to the class you want to test - I usually do it in the constructor - and, during the tests you have worked around the fact that something, in the real world would happen in background. You can, in other words, flatten the multiple threads in a single thread.
Example:
String message = "hello world!";
Executor executor = new DirectExecutor();
Chat chat = new Chat(executor);
// suppose that the sendMessage sends messages in background (async)
chat.sendMessage(message);
assertEqual(message, chatServer.lastMessageReceived());
Once we've used a DirectExecutor, we know that when we call sendMessage(), the execution of the logic behind it will now be synchronous. Then, the assertion at the next line can evaluate the result without waiting for the "background" process to complete. No sleeps and no thread coordination needed anymore.
The DirectExecutor as listed in the Javadocs can be improved to be more effective for testing purposes. For example, with Mockito you can implement a direct executor with a Mock Object that can also be queried to verify how the class under test interact with it.
// on the base test class (MockitoTestBase)
@Before
public void before() {
MockitoAnnotations.initMocks(this);
}
protected void implementAsDirectExecutor(ExecutorService executor) {
doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation)
throws Exception {
Object[] args = invocation.getArguments();
Runnable runnable = (Runnable)args[0];
runnable.run();
return null;
}
}).when(executor).submit(any(Runnable.class));
}
// on the subclass
@Mock private ExecutorService executor;
@Before
public void setUp() {
implementAsDirectExecutor(executor);
}
public void testChat() {
String message = "hello world!";
Chat chat = new Chat(executor);
// suppose that sendMessage sends messages in background (async)
chat.sendMessage(message);
// verify that the submit method has been invoked
Mockito.verify(executor, times(1)).sumit(...);
assertEqual(message, chatServer.lastMessageReceived());
}
In the above example I used an ExecutorService, which is a sub-interface of Executor, and provides additional features (like shutting down the executor and being able to reject tasks). But subsantially it's more or less the same.
Once we've tested that our class works fine with an instance of a DirectExecutor, we may assume that substituting it with a different one, our code will reasonably behave correctly. Of course, you still have to keep in mind that the task will be executed in the background and understand if you really covered your use case fully.
From http://en.newinstance.it/2010/09/07/workaround-to-multi-threaded-testing/
Opinions expressed by DZone contributors are their own.
Comments