How to unit test a code that is running in executor service? In my situation,
public void test() {
Runnable R = new Runnable() {
@Override
public void run() {
executeTask1();
executeTask2();
}
};
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(R);
}
When I am unit testing, I would like to make some validations that method executes.
I am executing this in an executor service, as it makes some network operations.
In my unit testing, I had to wait until this method finishes execution. Is there a better way I can do this, instead of waiting for Thread.sleep(500)
.
Unit testing code snippet:
@Test
public void testingTask() {
mTestObject.test();
final long threadSleepTime = 10000L;
Thread.sleep(threadSleepTime);
verify(abc, times(2))
.acquireClient(a, b, c);
verify(abd, times(1)).addCallback(callback);
}
Note: I am passing an executor service object into this constructor class. I would like to know if there is a good way of testing instead of waiting for sleep time.
When using an Executor, we can shut it down by calling the shutdown() or shutdownNow() methods. Although, it won't wait until all threads stop executing. Waiting for existing threads to complete their execution can be achieved by using the awaitTermination() method.
You can use the @Spy annotation. @RunWith(MockitoJUnitRunner. class) public class MadeUpClassNameTest{ @Spy private final ExecutorService executor = Executors. newFixedThreadPool(2); @Test ... }
To properly shut down an ExecutorService, we have the shutdown() and shutdownNow() APIs. The shutdown() method doesn't cause immediate destruction of the ExecutorService. It will make the ExecutorService stop accepting new tasks and shut down after all running threads finish their current work: executorService.
Spawn some threads. Have the main thread wait or sleep. Perform assertions from within the worker threads (which via ConcurrentUnit, are reported back to the main thread) Resume the main thread from one of the worker threads once all assertions are complete.
You could also implement an ExecutorService yourself that will run the task in the same thread. For example:
public class CurrentThreadExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
And then you could inherit from AbstractExecutorService
and use this implementation.
If you're using Guava, another easy one is to use MoreExecutors.newDirectExecutorService() since that does the same thing without you having to create one yourself.
Google Guava provides a great class called MoreExecutors
which helped me out when testing code that runs in parallel threads via Executor
or ExecutorService
in JUnit. It lets you create Executor
instances that just run everything in the same thread, essentially as a mock of a real Executor
. The issue is when things get run in other threads that JUnit isn't aware of, so these Executors
from MoreExecutors
make everything much easier to test since it's not actually parallel in another thread.
See the MoreExecutors
documentation https://google.github.io/guava/releases/19.0/api/docs/com/google/common/util/concurrent/MoreExecutors.html
You can modify your class constructor, or add a new constructor that you only use in tests, which lets you provide your own Executor
or ExecutorService
. Then pass in the one from MoreExecutors
.
So in the test file you'd create the mock executor using MoreExecutors
ExecutorService mockExecutor = MoreExecutors.newDirectExecutorService();
// or if you're using Executor instead of ExecutorService you can do MoreExecutors.newDirectExecutor()
MyService myService = new MyService(mockExecutor);
Then in your class, you only create a real Executor if it wasn't provided in the constructor
public MyService() {}
ExecutorService threadPool;
public MyService(ExecutorService threadPool) {
this.threadPool = threadPool;
}
public void someMethodToTest() {
if (this.threadPool == null) {
// if you didn't provide the executor via constructor in the unit test,
// it will create a real one
threadPool = Executors.newFixedThreadPool(3);
}
threadPool.execute(...etc etc)
threadPool.shutdown()
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With