如何在@Async void 方法中声明异常?

问题描述

我想声明一个应该在 @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 怎么样?

所以基本上当你执行抛出异常的方法时,你可以验证异常是在处理程序内部处理的吗?只是一个想法,没试过。