我想断言一个应该在@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");
}
}
我面临着同样的问题。
Bilak的帖子给出了用@Component
注释声明我的自定义AsyncUncaughtExceptionHandler
的想法。 然后,在我的定制AsyncConfigurer
中,我注入了我的定制AsyncUncaughtExceptionHandler
。
在我的测试中,我在自定义AsyncUncaughtExceptionHandler
上使用@MockBean
注释,因此我能够验证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 方法变成了异步方法,所以我不想只为测试更改返回值。
由于@Async
方法由来自asyncExecutor
的线程异步执行,并且由于对主线程没有任何影响的RuntimeException
而终止,因此实际Main-Test
线程在触发异步调用后成功与流的其余部分竞争一次。所以我建议使用 CompletableFuture 来保存异步进程的引用,无论它是否需要,并且真实地在测试用例中有所帮助
@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");
}
}
因此,在测试中,您可以等待线程完成Async
从 ExecutionException 断言原因,因为get
方法会抛出ExecutionException
如果将来异常完成
CompletableFuture.allOf(wait);
还有一点说明,您可以参考链接来断言包装的异常
使用将为AsyncConfigurer定义的AsyncUncaughtExceptionHandler怎么样?
所以基本上当你执行抛出异常的方法时,你可以验证异常是否在处理程序中处理?只是一个想法,没有尝试过这个。