问题描述
我想声明一个应该在 @Async
void 方法中抛出的异常。
以下失败,即使我已经添加了一个 SyncTaskExecutor
显式。
org.opentest4j.AssertionFailedError:预期抛出 RuntimeException,但没有抛出任何内容。
@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");
}
}
解决方法
由于 @Async
方法被来自 asyncExecutor
的线程异步执行,并且由于 RuntimeException
对主线程没有任何影响而终止,实际上 {{1 }} 线程在触发异步调用后与流程的其余部分成功竞争一次。所以我会建议使用 CompletableFuture 来始终保持异步进程的引用,即使它是否需要,也会如实帮助测试用例
Main-Test
因此在测试中您可以等待 @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");
}
}
线程完成断言来自 ExecutionException 的原因,因为如果此未来异常完成,Async
方法会抛出 get
>
ExecutionException
另外一个注意事项,您可以参考 link 来断言包装异常
,我面临同样的问题。
bilak 的帖子提出了使用 AsyncUncaughtExceptionHandler
注释声明我的自定义 @Component
的想法。
然后,在我对 AsyncConfigurer
的自定义实现中,我注入了我的自定义 AsyncUncaughtExceptionHandler
。
在我的测试中,我在我的自定义 @MockBean
上使用了 AsyncUncaughtExceptionHandler
批注,因此我能够验证 handleUncaughtException
是否在适当的异常情况下被调用。
代码示例:
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;
}
}
我的单元测试:
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());
}
}
我不确定这是否是正确的方法,但我有一个变成异步的 void 方法,所以我不想仅仅为了测试而改变返回值。
,使用为您的 AsyncUncaughtExceptionHandler 定义的 AsyncConfigurer 怎么样?
所以基本上当你执行抛出异常的方法时,你可以验证异常是在处理程序内部处理的吗?只是一个想法,没试过。