I've got a class HttpClient that has a function that returns CompletableFuture:
public class HttpClient {
public static CompletableFuture<int> getSize() {
CompletableFuture<int> future = ClientHelper.getResults()
.thenApply((searchResults) -> {
return searchResults.size();
});
return future;
}
}
Then another function calls this function:
public class Caller {
public static void caller() throws Exception {
// some other code than can throw an exception
HttpClient.getSize()
.thenApply((count) -> {
System.out.println(count);
return count;
})
.exceptionally(ex -> {
System.out.println("Whoops! Something happened....");
});
}
}
Now, I want to write a test to simulates that ClientHelper.getResults fails, so for that I wrote this:
#Test
public void myTest() {
HttpClient mockClient = mock(HttpClient.class);
try {
Mockito.doThrow(new CompletionException(new Exception("HTTP call failed")))
.when(mockClient)
.getSize();
Caller.caller();
} catch (Exception e) {
Assert.fail("Caller should not have thrown an exception!");
}
}
This test fails. The code within exceptionally never gets executed. However, if I run the source code normally and the HTTP call does fail, it goes to the exceptionally block just fine.
How must I write the test so that the exceptionally code is executed?
I got this to work by doing this in the test:
CompletableFuture<Long> future = new CompletableFuture<>();
future.completeExceptionally(new Exception("HTTP call failed!"));
Mockito.when(mockClient.getSize())
.thenReturn(future);
Not sure if this is the best way though.
Related
Java's handleAsync will not handle exceptions which don't come from a completion stage, for example:
package my.package;
import java.util.concurrent.CompletableFuture;
public class Test {
private CompletableFuture<String> throwWithoutCompletionStage() {
throw new RuntimeException("error");
}
private CompletableFuture<String> getName() {
// calls external API, does some work then throws
throwWithoutCompletionStage();
return CompletableFuture.completedFuture("name");
}
public CompletableFuture<String> process() {
return getName()
.thenApplyAsync(result -> {
// do something
return result;
})
.handleAsync((result, exception) -> {
if (exception != null) {
return result;
} else {
return null;
}
});
}
}
When process is called handleAsync will not be executed but rather the exception will be propagated to process's caller which is a bit confusing because one would think that handleAsync would catch the exception. So in this case I would also need to wrap process in try/catch to really catch all exceptions which looks weird in my opinion and is also error-prone: we need to always remember to wrap methods which return CompletableFuture in both handleAsync and try/catch.
Is there a best practice to prevent this double exception catching? One solution I thought of is to call supplyAsync which would create a completion stage and then use handleAsync on it:
public CompletableFuture<CompletableFuture<String>> process() {
return CompletableFuture.supplyAsync(() -> getName())
.handleAsync((result, exception) -> {
if (exception != null) {
return null;
} else {
return result;
}
});
}
The problem with this code is that now process return type is CompletableFuture<CompletableFuture<String>> but also this seems redundant to wrap code in supplyAsync solely in order to have handleAsync catch all exceptions.
If you don’t want to change the behavior of the getName() method, you have to use something like
public CompletableFuture<String> process() {
return CompletableFuture.supplyAsync(this::getName, Runnable::run)
.thenCompose(Function.identity())
.thenApplyAsync(result -> {
// do something
return result;
})
.handleAsync((result, exception) -> {
if (exception != null) {
return result;
} else {
return null;
}
});
}
By using Runnable::run as Executor you ensure that the “async” operation is executed immediately in the caller thread, just like the direct invocation of getName() would do. Using .thenCompose(Function.identity()), you get a CompletableFuture<String> out of the CompletableFuture<CompletableFuture<String>>.
However, if getName() returns a completed future in the successful case, it should also return a future in the exceptional case. This may look like
private CompletableFuture<String> getName() {
try {
throwWithoutCompletionStage();
return CompletableFuture.completedFuture("name");
} catch(Throwable t) {
return CompletableFuture.failedFuture(t);
}
}
failedFuture has been introduced in Java 9. If you need a Java 8 compatible solution, you have to add such a factory method to your code base
public static <U> CompletableFuture<U> failedFuture(Throwable ex) {
CompletableFuture<U> f = new CompletableFuture<>();
f.completeExceptionally(ex);
return f;
}
Or you integrate this logic into the getName() method:
private CompletableFuture<String> getName() {
CompletableFuture<String> result = new CompletableFuture<>();
try {
throwWithoutCompletionStage();
result.complete("name");
} catch(Throwable t) {
result.completeExceptionally(t);
}
return result;
}
I have the following methods:
#EnableAsync
#Service
Class MyService{
private String processRequest() {
log.info("Start processing request");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("Completed processing request");
return RESULT;
}
#Async
public CompletableFuture<String> getSupplyAsyncResult(){
CompletableFuture<String> future
= CompletableFuture.supplyAsync(this::processRequest);
return future;
}
#Async
public CompletableFuture<String> getCompletedFutureResult(){
CompletableFuture<String> future
= CompletableFuture.supplyAsync(this::processRequest);
return future;
}
and the following endpoints in controller:
#RequestMapping(path = "/asyncSupplyAsync", method = RequestMethod.GET)
public CompletableFuture<String> getValueAsyncUsingCompletableFuture() {
log.info("Request received");
CompletableFuture<String> completableFuture
= myService.getSupplyAsyncResult();
log.info("Servlet thread released");
return completableFuture;
}
and
#RequestMapping(path = "/asyncCompletable", method = RequestMethod.GET)
public CompletableFuture<String> getValueAsyncUsingCompletableFuture() {
log.info("Request received");
CompletableFuture<String> completableFuture
= myService.getCompletedFutureResult();
log.info("Servlet thread released");
return completableFuture;
}
Why would anyone use completableFuture.supplyAsync within #Async method in Spring endpoint?
I assume using completableFuture.completedFuture is more appropriate, please share your views.
They serve entirely different purposes to begin with. Before you think about how much it takes one or the other to process, you might want to understand how they work, first (so little calls are no indication of slow/fast anyway; these numbers mean nothing in this context).
Here is the same example you have:
public class SO64718973 {
public static void main(String[] args) {
System.out.println("dispatching to CF...");
//CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> processRequest());
CompletableFuture<String> future = CompletableFuture.completedFuture(processRequest());
System.out.println("done dispatching to CF...");
future.join();
}
private static String processRequest() {
System.out.println("Start processing request");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Completed processing request");
return "RESULT";
}
}
You can run this and then change the implementation (by uncommenting CompletableFuture.supplyAsync) and see where those System.out.println occur. You will notice that completedFuture will block main thread until it is executed, while supplyAsync will run in a different thread. So it's not like one is wrong and one is not, it depends on your use-case.
In general, it is not a great idea to use CompletableFuture.supplyAsync without configuring a pool for it; otherwise it will consume threads from ForkJoinPool.
I have to write Junit test for the catch block. But I am not able to identify what should I assert here. Since the func() is only catching the exception and not throwing anything I cannot assert using Assertions.assertThatExceptionOfType(). I am new to Junit testing so cannot think of anything else. Any possible way to test the type of exception received by catch block.
Method
public void func() {
try {
int x = solve();
} catch(Exception1 e) {
log.warn("error", e);
} catch(Exception2 e) {
log.warn("error", e);
}
}
private int solve() throws ExceptionName {
//do something...
throws new Exception("error occured");
...
}
You can change the visibility of solve() method and test it with all exception cases. For example change it to default
int solve() throws ExceptionName {
Put tests in the same package as class with this method so that it could be access from test.
UPDATE
The best way would be to change the code to be more testable as it was shown above.
In order to not change the code you can use the way from this answer. It can be tricky. With Mockito and PowerMockito you can control when Exception1 or Exception2 are creating. Based on this you will know which catch statement was executed.
In test code it could be like this:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ Exception1.class, Exception2.class, MyClass.class })
public class TestClass {
#Before
public void setup() {
Exception1 cutMock = Mockito.mock(Exception1.class);
PowerMockito.whenNew(Exception1.class)
.withArguments(Matchers.anyString())
.thenReturn(cutMock);
}
#Test
public void testMethod() {
// prepare
MyClasss myClass = new MyClass();
// execute
myClass.func();
// checks if the constructor has been called once and with the expected argument values:
String value = "abc";
PowerMockito.verifyNew(Exception1.class).withArguments(value);
}
}
Suppose I have a two classes that work together to execute a callable like this:
public class blah {
#Autowired
private ExecutorServiceUtil executorServiceUtil;
#Autowired
private RestTemplate restClient;
public SomeReturnType getDepositTransactions(HttpHeaders httpHeaders) {
ExecutorService executor = executorServiceUtil.createExecuter();
try {
DepositTransactionsAsyncResponse asyncResponse = getPersonalCollectionAsyncResponse( httpHeaders, executor);
// do some processing
// return appropriate return type
}finally {
executorServiceUtil.shutDownExecutor(executor);
}
}
Future<ResponseEntity<PersonalCollectionResponse>> getPersonalCollectionAsyncResponse( HttpHeaders httpHeaders, ExecutorService executor) {
PersonalCollectionRequest personalCollectionRequest = getpersonalCollectionRequest(); // getPersonalCollectionRequest populates the request appropriately
return executor.submit(() -> restClient.exchange(personalCollectionRequest, httpHeaders, PersonalCollectionResponse.class));
}
}
public class ExecutorServiceUtil {
private static Logger log = LoggerFactory.getLogger(ExecutorServiceUtil.class);
public ExecutorService createExecuter() {
return Executors.newCachedThreadPool();
}
public void shutDownExecutor(ExecutorService executor) {
try {
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
}
catch (InterruptedException e) {
log.error("Tasks were interrupted");
}
finally {
if (!executor.isTerminated()) {
log.error("Cancel non-finished tasks");
}
executor.shutdownNow();
}
}
}
How can I use Mockito to stub the a response and return it immediately?
I've tried the below but my innovcation.args() returns [null]
PowerMockito.when(executor.submit(Matchers.<Callable<ResponseEntity<OrxPendingPostedTrxCollectionResponseV3>>> any())).thenAnswer(new Answer<FutureTask<ResponseEntity<OrxPendingPostedTrxCollectionResponseV3>>>() {
#Override
public FutureTask<ResponseEntity<OrxPendingPostedTrxCollectionResponseV3>> answer(InvocationOnMock invocation) throws Throwable {
Object [] args = invocation.getArguments();
Callable<ResponseEntity<OrxPendingPostedTrxCollectionResponseV3>> callable = (Callable<ResponseEntity<OrxPendingPostedTrxCollectionResponseV3>>) args[0];
callable.call();
return null;
}
});
You do that by not using your ExecutorServiceUtil in your test code. What I mean is: you provide a mock of that util class to your production code!
And that mock does return a "same thread executor service"; instead of a "real service" (based on a thread pool). Writing such a same-thread-executor is actually straight forward - see here.
In other words: you want two different unit tests here:
You write unit tests for your ExecutorServiceUtil class in isolation; make sure it does the thing it is supposed to do (where I think: checking that it returns a non-null ExecutorService is almost good enough!)
You write unit tests for your blah class ... that use a mocked service. And all of a sudden, all your problems around "it is async" go away; because the "async" part vanishes in thin air.
I have a test of communication over a TCP socket where I expect the server to not respond within a set time frame when I send a certain message.
The servers behaviour is nothing I can control.
I know how to fail a test if it has not completed within a set time frame. But how can I do the opposite, make it pass for not completing within the timeframe?
I can use #Test (timeout=1000) to make a test fail if not complete within a second.
But, using Junit 4, is there a function to test for an expected timeout as a positive result? I.e. The test will fail if completed within the time frame and pass if not?
Good question. Actually you can do this using only junit tools. My idea is to inverse Timeout rule behaviour + use expected attribute of Test annotation. The only one limitation: you have to place your test in separate class, because Rule applies to all tests inside it:
public class Q37355035 {
private static final int MIN_TIMEOUT = 100;
#Rule
public Timeout timeout = new Timeout(MIN_TIMEOUT) {
public Statement apply(Statement base, Description description) {
return new FailOnTimeout(base, MIN_TIMEOUT) {
#Override
public void evaluate() throws Throwable {
try {
super.evaluate();
throw new TimeoutException();
} catch (Exception e) {}
}
};
}
};
#Test(expected = TimeoutException.class)
public void givesTimeout() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
}
}
I'm building upon the great answer by Andremoniy, don't forget to upvote his answer if you like this one!
I used the following modification to skip a test if it doesn't finish in the expected time frame. The benefit of this is that the test will be marked as Skipped by JUnit instead of Successful. This is good for optimistic execution of tests which can sometimes hang or not finish fast enough, but you don't want to mark them as failed or delete them.
public class OptimisticTestClass {
private static final int TEST_METHOD_TIMEOUT_MS = 100;
#Rule
public Timeout timeout = new Timeout(TEST_METHOD_TIMEOUT_MS, TimeUnit.MILLISECONDS) {
public Statement apply(Statement base, Description description) {
return new FailOnTimeout(base, TEST_METHOD_TIMEOUT_MS) {
#Override
public void evaluate() throws Throwable {
try {
super.evaluate();
} catch (TestTimedOutException e) {
Assume.assumeNoException("Test did not finish in the allocated time, skipping!", e);
}
}
};
}
};
// The test times out and is skipped
public void givesTimeout() throws InterruptedException {
Thread.sleep(1000);
}
}
It's simpler in Java 9
CompletableFuture.supplyAsync(() -> dormammu.bargain())
.orTimeout(1, TimeUnit.SECONDS)
.handle((result, throwable) -> {
if (!(throwable instanceof TimeoutException)) {
Assertions.fail();
}
return result;
}).get();
If the method doesn't return within 1 second, it will time out. In handle method you can make sure TimeoutException was thrown, otherwise fail the test.