I want to assert an exception that should be thrown within an #Async void method.
The following fails, even though I already add a SyncTaskExecutor explicit.
org.opentest4j.AssertionFailedError: Expected RuntimeException to be thrown, but nothing was thrown.
#TestConfiguration
public class SyncTaskExecutorTestConfiguration {
#Bean
#Primary
public TaskExecutor asyncExecutor() {
return new SyncTaskExecutor();
}
}
#SpringBootTest
#Import(SyncTaskExecutorTestConfiguration.class)
public class MyTest {
#Test
public void test() {
assertThrows(RuntimeException.class, () -> service.run());
}
}
#Service
#Async //also #EnableAsync existing on #Configuration class
public class AsyncService {
public void run() {
//of course real world is more complex with multiple sub calls here
throw new RuntimeException("junit test");
}
}
I'm facing the same problem.
bilak's post gave the idea of having my custom AsyncUncaughtExceptionHandler declared with a #Component annotation.
Then, in my custom implmentation of AsyncConfigurer I was injecting my custom AsyncUncaughtExceptionHandler.
In my tests, I used the #MockBean annotation on my custom AsyncUncaughtExceptionHandler, so I was able to verify that the handleUncaughtException was called with the appropriate exception.
Code sample:
AsyncExceptionHandler
#Slf4j
#Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.error("Exception while executing with message: {} ", throwable.getMessage());
log.error("Exception happen in {} method ", method.getName());
}
}
CustomAsyncConfigurer
#Configuration
public class CustomAsyncConfigurer implements AsyncConfigurer {
final private AsyncExceptionHandler asyncExceptionHandler;
#Autowired
public TaskExecutorConfiguration(AsyncExceptionHandler asyncExceptionHandler) {
this.asyncExceptionHandler = asyncExceptionHandler;
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("AsyncThread::");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncExceptionHandler;
}
}
My unit test:
class FooServiceTest extends FooApplicationTests {
#MockBean
private AsyncExceptionHandler asyncExceptionHandler;
#Autowired
private FooService fooService;
#Test
void testCreateEnrollmentBioStoreException() throws Exception {
fooService.doBar();
ArgumentCaptor<FooException> argumentCaptor = ArgumentCaptor.forClass(FooException.class);
verify(asyncExceptionHandler, times(1)).handleUncaughtException(argumentCaptor.capture(), any(), any());
FooException exception = argumentCaptor.getValue();
assertEquals("Foo error message", exception.getMessage());
}
}
I'm not sure if this is the right way, but I have a void method that was turned into async, so I didn't want to change the return value just for the tests.
Since the #Async method get executed asynchronously by a thread from asyncExecutor and it is terminated due to RuntimeException which doesn't have any impact on Main thread, the actually Main-Test thread competes successfully with the rest of flow once after it trigger the async call. So i will recommend to use the CompletableFuture to hold the reference of Async process always even it's required or not and truthfully will help in test cases
#Service
#Async
public class AsyncService {
public CompletableFuture<Void> run() {
//of course real world is more complex with multiple sub calls here
throw new RuntimeException("junit test");
}
}
So in the test you can wait for Async thread to complete assert the cause from ExecutionException, Since the get method throws ExecutionException if this future completed exceptionally
CompletableFuture.allOf(wait);
One more note you can refer link for asserting wrapped exceptions
What about using AsyncUncaughtExceptionHandler that will be defined for your AsyncConfigurer?
So basically when you execute your method which throws exception you can verify that exception was handled inside handler? Just an idea, didn't tried this.
Related
in my spring project I created simple aspect which catches DaoExceptions and turns them into service exceptions
#Aspect
#Component
public class ExceptionAspect {
#Pointcut("execution(public * *(..))")
private void allPublicMethods() {
}
#Pointcut("within(img.imaginary.dao.*)")
private void inDao() {
}
#AfterThrowing(pointcut = "allPublicMethods() && inDao()", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, DaoException exception) {
throw new ServiceException(String.format("%s in method %s %s class", exception.getMessage(),
joinPoint.getSignature().getName(), joinPoint.getTarget().getClass().getSimpleName()), exception);
}
}
and it works fine when DaoException throwing from dao layer to service it turns into Service exception
but only not in the tests:
#Test
void findById_ShouldThrowServiceException_WhenEntityNotFound() {
Mockito.when(defaultDao.findById(0)).thenThrow(DaoException.class);
assertThrows(ServiceException.class, () -> defaultServiceImpl.findById(0));
}
in this test I have a defaultDao and it is a Mock object and when it throws a DaoException my aspect does not catch and proxy it
I can't figure out how to solve this problem
With the following assumptions - it is a spring-boot project , defaultDao is a mocked bean using #MockBean
Do go through the issue to understand why #MockBean will not work.
Following code would mock a bean and throw an exception . You will need to adjust the code to make it work for you.( eg: #SpringBootTest may or may not be required for your case ).
#SpringBootTest
class DefaultDaoTest {
#Autowired
DefaultDao defaultDao;
#Test
void test() {
assertThrows(ServiceException.class, () -> defaultDao.findById(0));
}
}
#Configuration
class TestConfig {
#Bean
#Primary
public DefaultDao defaultDao() {
return new DefaultDao() {
#Override
public Object findById(Long id) {
throw new DaoException();
}
};
}
}
I have the following classes/interfaces:
public interface Api {
Mono<Object> update();
}
#Service
public class Service {
#Autowired
private Api api;
private List<Object> list;
public void update() {
api.update().subscribe(result -> list.add(result), Throwable::printStackTrace);
}
}
I'm trying to perform a unit test using Mockito of the consumer function when I subscribe to the Mono that is being returned by my Api. So far, my test class looks as follows
#RunWith(SpringRunner.class)
#SpringBootTest
public class ServiceTest {
#Mock
private Mono<Object> mono;
#MockBean
private Api api;
#Autowired
private Service service;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testUpdate() {
when(api.update()).thenReturn(mono);
doAnswer((Answer<Void>) invocation -> {
System.out.println("FOO");
Consumer<Object> consumer = invocation.getArgument(0);
consumer.accept(testObject)
return null;
}).when(mono).subscribe(any(Consumer.class), any(Consumer.class));
service.update();
}
}
First, I am seeing that the code inside doAnswer() never gets invoked. Secondly, since Mockito's any() returns null and the error consumer of subscribe function expects non-null I'm running into NullPointerException when I run my test.
What would be the best approach of going about unit testing my subscribe's consumer function?
I am having a hard time understanding the issue at hand, which I believe is a problem with the way how Spring proxies get created.
In this minimal example, I have two classes, AccountLoader and BankImpl, which implements an interface Bank. Upon start-up, AccountLoader executes some concurrent calls to an autowired Bank-instance, where the method in BankImpl is advised with an aspect.
In this setup the call to complete the future (Future.get) finishes with a TimeoutException, because the call appears to never terminate. However, if I call the same method before the callables get submitted to the executor, all calls finish successfully.
What is going on with Spring here? Why does this async call not terminate? And why in all seven hells does it terminate, if I add a synchronous call before the async one?
You may find the code below, a complete working example is also available on Github
public interface Bank {
Map<String, String> getAccounts(String q);
}
The simple implementation
#Service
public class BankImpl implements Bank {
private static final Logger LOGGER = LoggerFactory.getLogger(BankImpl.class);
#Override
public Map<String, String> getAccounts(String q) {
LOGGER.info("Listing accounts for {}", q);
return Collections.singletonMap(q, "q");
}
}
And finally the caller
#Service
public class AccountLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountLoader.class);
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
#Autowired
private Bank bank;
#PostConstruct
public void refresh() {
LOGGER.info("Refreshing accounts");
// Uncommenting the following line will let the calls terminate
// bank.getAccounts("sync");
try {
executorService.submit(() -> { bank.getAccounts("async"); })
.get(5L, TimeUnit.SECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
For the sake of completeness, here are the aspect
#Aspect
#Component
public class SomeAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(SomeAspect.class);
#AfterReturning(pointcut = "execution(* com.github.mtritschler.aspects.BankImpl.getAccounts(..))", returning = "returnValue")
public Map<String, String> logCallee(Map<String, String> returnValue) {
LOGGER.info("Result is {}", returnValue);
return returnValue;
}
}
and last but not least the configuration
#EnableAspectJAutoProxy
#Configuration
public class MyConfig {
}
Update: if I remove the #EnableAspextJAutoProxy, I also don't get an exception. Switching to load-time weaving did not change anything either.
It turned out that there was a race condition between the application initialization in the main thread and the concurrent access to the injected dependency.
Once we switched the #PostConstruct for a listener on ContextRefreshedEvent it worked just fine.
Using Spring Boot 1.5.2.RELEASE and the #Async annotation seems to be ignored.
Have setup the environment like this:
#SpringBootApplication
#EnableAsync
public class Application extends AsyncConfigurerSupport {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("async-task-");
executor.initialize();
return executor;
}
...
... the async method itself:
#Service
public class MyService {
#Async
public Future<Long> asyncTask() throws Exception {
Long test = 1023L;
Thread.sleep(10000);
return new AsyncResult<>(test);
}
}
... now I'm trying to use this:
#RestController
public MyController {
#Autowired
public MyService myService;
#PostMapping("/test")
public ResponseEntity<MyResponse> test() {
return new ResponseEntity<>(
new MyResponse(myService
.asyncTask()
.get()),
HttpStatus.OK);
}
}
... and the controller method still hangs for 10sec instead of to be immediatelly returned.
The #Async method is called from the different object. It's neither private nor transactional one as it mentioned at the similar questions.
How to let the method to be invoked asynchronously?
You should have a look at the
Future#get javadoc:
Waits if necessary for the computation to complete, and then retrieves
its result.
You are transforming your async method to a synchronous call by calling get method.
Thus instead of calling get, just return the Future. Spring MVC supports future as return type:
A ListenableFuture or CompletableFuture/CompletionStage can
be returned when the application wants to produce the value from a
thread pool submission.
Example:
return myService.asyncTask().thenApply(r -> ResponseEntity.ok(new MyResponse(r)));
Your test() function is invoking get() on the Future instance. The documentation for this function states: "Waits if necessary for the computation to complete, and then retrieves its result."
So rather than invoking get(), you likely want to return some sort if ID that the caller can use to retrieve the result at a later time (or switch to a synchronous response).
This is very similar to the other question here: Spring Boot #Async method in controller is executing synchronously. However my #Service method annotated with #Async is still executing synchronously. I've tried all methods from different forums to no use. Hopefully someone could help me figure out why. A simple spring boot project as below doesn't work.
AsyncConfiguration.java
#Configuration
#EnableAsync
public class AsyncConfiguration(){}
SomeService.java
#Service
public class SomeService() {
#Async
public void doSomething() {
try {
Thread.sleep(5000L);
} catch (Exception ignore){}
}
}
SomeController.java
#Controller
public class SomeController() {
#Inject SomeService someService;
#RequestMapping(value="/", method=RequestMethod.POST)
public String doStuff() {
someService.doSomething();
return "mytemplate";
}
}
Here is a simple example with #Async. Follow these steps to get #Async to work in your Spring Boot application:
Step 1: Add #EnableAsync annotation and Add TaskExecutor Bean to Application Class.
Example:
#SpringBootApplication
#EnableAsync
public class AsynchronousSpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(AsynchronousSpringBootApplication.class);
#Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(AsynchronousSpringBootApplication.class,args);
}
}
Step 2: Add Method which executes an Asynchronous Process
#Service
public class ProcessServiceImpl implements ProcessService {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
#Async("processExecutor")
#Override
public void process() {
logger.info("Received request to process in ProcessServiceImpl.process()");
try {
Thread.sleep(15 * 1000);
logger.info("Processing complete");
}
catch (InterruptedException ie) {
logger.error("Error in ProcessServiceImpl.process(): {}", ie.getMessage());
}
}
}
Step 3: Add an API in the Controller to execute the asynchronous processing
#Autowired
private ProcessService processService;
#RequestMapping(value = "ping/async", method = RequestMethod.GET)
public ResponseEntity<Map<String, String>> async() {
processService.process();
Map<String, String> response = new HashMap<>();
response.put("message", "Request is under process");
return new ResponseEntity<>(response, HttpStatus.OK);
}
I have also written a blog and a working application on GitHub with these steps. Please check:
http://softwaredevelopercentral.blogspot.com/2017/07/asynchronous-processing-async-in-spring.html
Sorry for my English.
The same problem happened to me.
The solution was to add
#Autowired
private SomeService someService;
In the Controller class, in this way it allows the class to contain the Beam Configurations associated with "SomeService", thus being able to execute the asynchronous method perfectly.
Here is a project with a functional asynchronous method:
https://github.com/JColmenares/async-method-api-rest.git