SpringBootTest集成@Async
问题:程序中使用@Async异步执行操作,但是在Mock测试中需要调用该方法,并且需要获取异步执行之后的数据,如何保持数据一致性与测试效率。
首先肯定不能说拷贝异步方法块之后去掉@Async,生成一模一样的同步方法专门供mock 测试类使用,这样子会造成很多重复代码,维护成本极高,并且无法保证代码测试覆盖率。
第二个是我最初使用的,在mock发送请求后,Thread.sleep()强制线程休眠。这个度并不好控制,异步方法使用初衷就是暗地处理耗时长,对正常系统操作时效性无影响的method,要设置休眠时间只能往大了加,而且肯定是越加越大,并且测试类只会越来越多,导致部署时跑测试类的时间无限延长。
一直在寻找解决方法,这里必须要吐槽国产GPT,RZ文心一言,以及各种奇奇怪怪的GPT,居然凭空捏造了@DisableAsync和@ExcludeBean 各种注解,真的是搁着玩文字游戏的。
找了一晚上,总算找到个靠谱的大佬,靠着一手研究源码得出了妙招。
简单的说就是在测试类中import一个配置类,在我们调用异步方法时,(由于@Async并不是auto-configuration,在新开线程执行异步时才获取连接池实例),这时候我们将异步的连接池变成一个同步的线程去run,相当于配置DisableAsync。
import java.util.concurrent.Executor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.stereotype.Component;
@Component
public class DisableAsyncForTestConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
// 测试环境下立即执行异步任务
return new org.springframework.core.task.SyncTaskExecutor();
}
}
测试类中Import
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Import({DisableAsyncForTestConfig.class})
@SpringBootTest
class disableAsyncTest{
}
同时,SpringBootApplication启动类中,需要额外exclude DisableAsyncForTestConfig.class,保证运行程序时正常开启异步线程
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@SpringBootApplication
//exclude this async configuration for enable async
@ComponentScan(excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = DisableAsyncForTestConfig.class)})
public class SpringBootApplication {
}
打完收工 —20230722 01:41 Sat.