How to mock completablefuture.get()? - java

This is the method I'm trying to mock:
#VisibleForTesting
public List<Row> processRows2(CompletableFuture future) {
List<Row> rows2 = new ArrayList<>();
try {
DefaultAsyncResultSet beep = (DefaultAsyncResultSet) future.get();
for (Row b : beep.currentPage()) {
rows2.add(b);
}
}
catch (ExecutionException | InterruptedException e) {
LOGGER.error(e);
LOGGER.error(e.getStackTrace());
throw new RuntimeException(e.getMessage() + " - Check thread pool resources are enough, may be too many queries in queue");
}
return rows2;
}
The problem is that when I try to test it with this (currently just trying to get it to run all the way to either success or failure):
#Test
public void processRows2test() {
FeatureDaoImpl gar = new FeatureDaoImpl(connection);
CompletableFuture L = new CompletableFuture();
gar.processRows2(L);
}
It hangs endlessly. My guess is that the future.get() is where it's hanging; I'm not sure. But I'm not sure how to mock that. I've tried this:
#Mock
private CompletableFuture mockFutures;
#Before
public void setUp() {
try {
Mockito.when(mockFutures.get()).thenReturn((AsyncResultSet) mockResultSetFuture);
}
catch (Exception e) {
}
}
But this I feel is not correct. The try catch is because it yells at me about unhandled exceptions on the get(), so I don't know how to get around that.
I have also now tried this:
#Mock
final CompletableFuture<List<String>> mockedFuture = Mockito.mock(CompletableFuture.class);
With the following in the setup:
Mockito.doReturn(new ArrayList<Row>()).when(mockedFuture).get();
But it still hangs endlessly.
I've seen these:
How to mock completion of a CompletableFuture in Mockito
This one I don't understand what exactly it's trying to get me to do, and doesn't feel super applicable, because it's not a get method. I saw some examples here that have .get() in them... but none were mocked methods unfortunately, they were gets in the test itself: https://www.javatips.net/api/java.util.concurrent.completablefuture
EDIT: the code runs. It returns results. So it isn't that the actual method isn't returning a value - I know it does this, it's doing it in QA right now.

Your current CompletableFuture is not completed, so the .get() method hangs waiting for async completion that will never happen. You can use CompletableFuture.completedFuture(value) to create a CompletableFuture instance that will return the passed value when .get() is called on it.

You can use the CompletableFuture.completedFuture method here
#Test
public void processRows2test() {
FeatureDaoImpl gar = new FeatureDaoImpl(connection);
CompletableFuture L = CompletableFuture.completedFuture(new ArrayList<Row>());
gar.processRows2(L);
}

Related

Is CompletableFuture followed immediately by a get efficient?

I just found the following code, it supplies an asynchronous task but immediately get the result (so if I understand correctly, it blocks the current thread until the result is available).
Is it efficient ?
public String myMethod() {
CompletableFuture<String> futur = CompletableFuture.supplyAsync(() -> {
// my long call to an external API
return "theResult";
});
try {
return future.get(FUTURE_TIMEOUT_DURATION, TimeUnit.MINUTES);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
If the timeout is handled correctly in the call to the external API, do I need this completable futur ?
Is it ok to simplify the code like this ?
public String myMethod() {
// my long call to an external API
return "theResult";
}
If you doesn't expect any problem with timeout you most probably can remove code related to feature.
There is possibility that code use some threadlocal variables or otherwise relies on being executed in separate thread.

Handling CompletableFuture exceptions in controller

I'm trying to get into CompletableFuture class for a project I'm running, and I got to some question here:
There is the following method: it tries to find a conversation by its ID or hash; and, if not found, it throws an exception. So far, so good.
public ConversationOutput getConversationByIdOrHash(String conversationIdOrHash)
throws ConversationNotFoundException {
Conversation conversation = this.conversationRepository.getByIdOrHash(conversationIdOrHash);
if (conversation == null) {
throw new ConversationNotFoundException(conversationIdOrHash);
}
return this.modelMapper.map(conversation, ConversationOutput.class);
}
Note that I am throwing ConversationNotFoundException from my method signature. My SpringBoot controller is reacting to this exception and it's all working fine since the beginning.
What I'm trying to do is to make this to a CompletableFuture return and actually throwing an exception, something similar to:
public CompletableFuture<ConversationOutput> getConversationByIdOrHashAsync(String conversationIdOrHash)
throws ConversationNotFoundException {
return CompletableFuture.supplyAsync(() -> this.getConversationByIdOrHash(conversationIdOrHash));
}
I've seen posts where people use exceptionally to handle exceptions, but what I really want to do is to throw it to my controller and let it handle it. Any suggestions of how can I make it?
Thank you all!
The question is do you care about the result of CompletableFuture.
CompletableFuture is like a special task and it is processed on other thread. If you don't invoke .join() you won't receive the results of CompletableFuture. This method also will propagate the exception if any occured. However it waits for CompletableFuture to finish and blocks the request.
However, there is no way to get exceptions from the inside of the CompletableFuture without waiting, you have to treat it like other task.
You can pass the completed future in case of a success, and failed future along with your custom exception.
public CompletableFuture<ConversationOutput> getConversationByIdOrHashAsync(String conversationIdOrHash) {
try {
return CompletableFuture.completedFuture(this.getConversationByIdOrHash(conversationIdOrHash));
} catch (ConversationNotFoundException e) {
return CompletableFuture.failedFuture(e);
}
}
and then at your controller level you can handle the exception.
final CompletableFuture<ConversationOutput> future = getConversationByIdOrHashAsync("idOrHash");
future.whenComplete((r, e) -> {
if (e != null) {
if (e instanceof ConversationNotFoundException) {
//handling
}
}
});

How to write test cases for ExecutorServices?

I have a method which is using ExecutorService to create and execute multiple threads. Code works fine. Now I want to write JUNIT test case for the same.
Code is as below :
private void method(Long input1, object result)
{
ExecutorService executor = Executors.newFixedThreadPool(4);
List<Callable< myObject>> callables = Arrays.asList( callables as List);
try
{
List<Future< myObject>> futures = executor.invokeAll(callables);
executor.shutdown();
for(Future< myObject> f : futures)
{
method(f, result);
}
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I am new to JUnit test cases. I have looked for answers on internet but there are no specific answers to my problem.
I need a help to write test case so that it comes under Jacoco coverage. Thanks in advance.
With the help of PowerMockito you can mock the static method:
Executors.newFixedThreadPool.
ExecutorService executors = Mockito.mock(ExecutorService.class);
Mockito.when(executors.invokeAll()).thenReturn( <list of futures> );
PowerMockito.mockStatic(Executors.class);
PowerMockito.when(Executors.newFixedThreadPool(anyInt())).thenReturn(executors);
Aim to use a real list containing real Future's (if possible).
(Also do not forget to put the correct classes into the #PrepareForTest Annotation)
Whether such a test makes sense depends on what method(f, result); is doing. If we assume its a private method, there is probably no other way of testing/covering it.
Also its probably not a good idea to have the submission of tasks to an ExecutorService and the handling of the results in a blocking method, so consider a refactoring anyway.

Mockito verify fails in second unit test

I am using Mockito together to JUnit to implement unit tests for a class in an Android project.The problem is that I call Mockito.verify in two consequent tests where the tests are exactly same (to make sure that I am using Mockito correctly) but the interesting thing is that verify in second test always fails.I suspect that some operations need to be done before each test using #before annotation or so, that I have missed.Here are some code snippet about what I am doing.
I use Android Studio 3.4.1, Mockito 2.7.22 and JUnit 4.12.
#Test
public void test_onStart_do_nothing() throws Exception {
ZConnectionService zConnectionService = new ZConnectionService();
ZConnection mockedZConnection = mock(ZConnection.class);
doNothing().when(mockedZConnection).connect();
zConnectionService.initConnection(mockedZConnection);
verify(mockedZConnection, times(1)).connect();
}
#Test
public void test_onStart_throw_IO_exceptioon() throws Exception {
ZConnectionService zConnectionService = new ZConnectionService();
ZConnection mockedZConnection = mock(ZConnection.class);
doNothing().when(mockedZConnection).connect();
zConnectionService.initConnection(mockedZConnection);
// Line above is the line that error message points to!
verify(mockedZConnection, times(1)).connect();
}
Here comes the function under test
public void initConnection(ZConnection connection) {
Log.d(TAG,"initConnection()");
if (mConnection == null) {
mConnection = connection;
}
if (!mActive) {
mActive = true;
if (mThread == null || !mThread.isAlive()) {
mThread = new Thread(new Runnable() {
#Override
public void run() {
// The code here runs in a background thread.
Looper.prepare();
mTHandler = new Handler();
try {
mConnection.connect();
} catch (IOException e) {
Intent i = null;
i = new Intent(ZConnectionService.UI_NOTCONNECTED);
i.setPackage(getApplicationContext().getPackageName());
getApplicationContext().sendBroadcast(i);
e.printStackTrace();
// Stop the services all together.
stopSelf();
}
Looper.loop();
}
});
mThread.start();
}
}
}
I expect that both tests should pass without any problem. In fact, both tests are passed when I ran them individually, but they fail when I run the whole suite and the error is:
Wanted but not invoked:
mockedZinkConnection.connect();
-> at com.app.z.ZConnectionServiceUnitTest.test_onStart_throw_IO_exceptioon(ZConnectionServiceUnitTest.java:207)
Actually, there were zero interactions with this mock.
I think the issue is a multithreading one.
When you call initConnection, it calls mConnection.connect() in a Thread
The problem you are having is that this Thread takes some time to complete and you end up calling verify(mockedZConnection, times(1)).connect(); before the Thread actually reached the connect() call.
A way to make sure about it is to join the Thread after you start it, it will wait until the Thread has finished before continuing:
mThread.start();
try {
mThread.join();
} catch (InterruptedException i) {
i.printStackTrace();
}
Now both tests should work.
This of course is not acceptable in the code, because it negated the use of a Thread. You will need an other way to test it.
A way I can think of would be to wait for the Thread to complete in your test before checking the mock:
#Test
public void test_onStart_throw_IO_exceptioon() throws Exception {
ZConnectionService zConnectionService = new ZConnectionService();
ZConnection mockedZConnection = mock(ZConnection.class);
doNothing().when(mockedZConnection).connect();
zConnectionService.initConnection(mockedZConnection);
// Wait for the Thread to complete
while(zConnectionService.mThread.isAlive()) {
Thread.sleep(100);
}
verify(mockedZConnection, times(1)).connect();
}
Tried and it works fine for me. Not sure it is a best practice though as you need to make public some internals of your class, which violates encapsulation
maybe having a package protected isThreadAlive() method on your ZConnectionService class could be acceptable
boolean isThreadAlive() {
return mThread.isAlive();
}
and the loop in the test
while(zConnectionService.isThreadAlive()) {
Thread.sleep(100);
}

CompletableFuture swallows exceptions?

I've been playing around with CompletableFuture and noticed a strange thing.
String url = "http://google.com";
CompletableFuture<String> contentsCF = readPageCF(url);
CompletableFuture<List<String>> linksCF = contentsCF.thenApply(_4_CompletableFutures::getLinks);
linksCF.thenAccept(list -> {
assertThat(list, not(empty()));
});
linksCF.get();
If, in my thenAccept call, the assertion fails, the exception is not propagated.
I tried something even uglier then:
linksCF.thenAccept(list -> {
String a = null;
System.out.println(a.toString());
});
nothing happens, no exception is propagated. I tried using methods like handle and others related to exceptions in CompletableFutures, but failed - none is propagating the exception as expected.
When I debugged the CompletableFuture, it does catch the exception like this:
final void internalComplete(T v, Throwable ex) {
if (result == null)
UNSAFE.compareAndSwapObject
(this, RESULT, null,
(ex == null) ? (v == null) ? NIL : v :
new AltResult((ex instanceof CompletionException) ? ex :
new CompletionException(ex)));
postComplete(); // help out even if not triggered
}
and nothing else.
I'm on JDK 1.8.0_05 x64, Windows 7.
Am I missing something here?
The problem is you never request to receive the results of your call to linksCF.thenAccept(..).
Your call to linksCF.get() will wait for the results of the execution in your chain. But it will only return the results of then linksCF future. This doesn't include the results of your assertion.
linksCF.thenAccept(..) will return a new CompletableFuture instance. To get the exception thrown call get() or check the exception status with isCompletedExceptionally() on the newly return CompletableFuture instance.
CompletableFuture<Void> acceptedCF = linksCF.thenAccept(list -> {
assertThat(list, not(empty()));
});
acceptedCF.exceptionally(th -> {
// will be executed when there is an exception.
System.out.println(th);
return null;
});
acceptedCF.get(); // will throw ExecutionException once results are available
Alternative?
CompletableFuture<List<String>> appliedCF = linksCF.thenApply(list -> {
assertThat(list, not(empty()));
return list;
});
appliedCF.exceptionally(th -> {
// will be executed when there is an exception.
System.out.println(th);
return Coolections.emptyList();
});
appliedCF.get(); // will throw ExecutionException once results are available
Although the question is basically already answered by Gregor Koukkoullis (+1), here is a MCVE that I created to test this.
There are several options for obtaining the actual exception that caused the problem internally. However, I don't see why calling get on the future that is returned by thenAccept should be an issue. In doubt, you could also use thenApply with the identity function and use a nice fluent pattern, like in
List<String> list =
readPage().
thenApply(CompletableFutureTest::getLinks).
thenApply(t -> {
// check assertion here
return t;
}).get();
But maybe there's a particular reason why you want to avoid this.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
public class CompletableFutureTest
{
public static void main(String[] args)
throws InterruptedException, ExecutionException
{
CompletableFuture<String> contentsCF = readPage();
CompletableFuture<List<String>> linksCF =
contentsCF.thenApply(CompletableFutureTest::getLinks);
CompletableFuture<Void> completionStage = linksCF.thenAccept(list ->
{
String a = null;
System.out.println(a.toString());
});
// This will NOT cause an exception to be thrown, because
// the part that was passed to "thenAccept" will NOT be
// evaluated (it will be executed, but the exception will
// not show up)
List<String> result = linksCF.get();
System.out.println("Got "+result);
// This will cause the exception to be thrown and
// wrapped into an ExecutionException. The cause
// of this ExecutionException can be obtained:
try
{
completionStage.get();
}
catch (ExecutionException e)
{
System.out.println("Caught "+e);
Throwable cause = e.getCause();
System.out.println("cause: "+cause);
}
// Alternatively, the exception may be handled by
// the future directly:
completionStage.exceptionally(e ->
{
System.out.println("Future exceptionally finished: "+e);
return null;
});
try
{
completionStage.get();
}
catch (Throwable t)
{
System.out.println("Already handled by the future "+t);
}
}
private static List<String> getLinks(String s)
{
System.out.println("Getting links...");
List<String> links = new ArrayList<String>();
for (int i=0; i<10; i++)
{
links.add("link"+i);
}
dummySleep(1000);
return links;
}
private static CompletableFuture<String> readPage()
{
return CompletableFuture.supplyAsync(new Supplier<String>()
{
#Override
public String get()
{
System.out.println("Getting page...");
dummySleep(1000);
return "page";
}
});
}
private static void dummySleep(int ms)
{
try
{
Thread.sleep(ms);
}
catch (InterruptedException e)
{
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}
If, in my thenAccept call, the assertion fails, the exception is not propagated.
The continuation that you register with thenAccept() is a separate task from the linksCF future. The linksCF task completed successfully; there is no error for it to report. It has its final value. An exception thrown by linksCF should only indicate a problem producing the result of linksCF; if some other piece of code that consumes the result throws, that does not indicate a failure to produce the result.
To observe an exception that happens in a continuation, you must observe the CompletableFuture of the continuation.
correct. but 1) I should not be forced to call get() - one of the points of the new constructs; 2) it's wrapped in an ExecutionException
What if you wanted to hand the result off to multiple, independent continuations using thenAccept()? If one of those continuations were to throw, why should that impact the parent, or the other continuations?
If you want to treat linksCF as a node in a chain and observe the result (and any exceptions) that happen within the chain, then you should call get() on the last link in the chain.
You can avoid the checked ExecutionException by using join() instead of get(), which will wrap the error in an unchecked CompletionException (but it is still wrapped).
The answers here helped me to manage exception in CompletableFuture, using "exceptionnaly" method, but it missed a basic example, so here is one, inspired from Marco13 answer:
/**
* Make a future launch an exception in the accept.
*
* This will simulate:
* - a readPage service called asynchronously that return a String after 1 second
* - a call to that service that uses the result then throw (eventually) an exception, to be processed by the exceptionnaly method.
*
*/
public class CompletableFutureTest2
{
public static void main(String[] args)
throws InterruptedException, ExecutionException
{
CompletableFuture<String> future = readPage();
CompletableFuture<Void> future2 = future.thenAccept(page->{
System.out.println(page);
throw new IllegalArgumentException("unexpected exception");
});
future2.exceptionally(e->{
e.printStackTrace(System.err);
return null;
});
}
private static CompletableFuture<String> readPage()
{
CompletableFuture<String> future = new CompletableFuture<>();
new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
// FUTURE: normal process
future.complete("page");
}).start();
return future;
}
}
The mistake to avoid is to call "exceptionnaly" on the 1st future (the variable future in my code) instead of the future returned by the "thenAccept" which contains the lambda that may throw an exception (the variable future2 in my code).
.
As usual, understanding the behavior of CompletableFuture is better left to the official docs and a blog.
Each then...() chaining method of the CompletableFuture class, which implements CompletionStage, accepts a an argument a CompletionStage. The stage that is passed depends on which order of then...() methods you've chained. Again, docs, but here's that aforementioned blog.

Categories