Java JUnit测试Spring@Async void服务方法
我有春季服务:Java JUnit测试Spring@Async void服务方法,java,spring,junit,spring-async,Java,Spring,Junit,Spring Async,我有春季服务: @Service @Transactional public class SomeService { @Async public void asyncMethod(Foo foo) { // processing takes significant time } } 我对这个SomeService进行了集成测试: @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConf
@Service
@Transactional
public class SomeService {
@Async
public void asyncMethod(Foo foo) {
// processing takes significant time
}
}
我对这个SomeService
进行了集成测试:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest
@Transactional
public class SomeServiceIntTest {
@Inject
private SomeService someService;
@Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
verifyResults();
}
// verifyResult() with assertions, etc.
}
问题是:
- assomeservice.asyncMethod(..)用
和@Async
- 因为
遵循SpringJUnit4ClassRunner
语义@Async
testAsyncMethod
线程将调用someService.asyncMethod(testData)
分叉到它自己的工作线程中,然后直接继续执行verifyResults()
在验证结果之前,如何等待someService.asyncMethod(testData)
的完成?请注意,此处不适用的解决方案,因为someService.asyncMethod(testData)
返回void
,而不是未来的,以便遵守@Async
语义,例如
为了解决我的问题,我引入了一个新的Spring配置文件非异步
如果非异步
配置文件处于非活动状态,则使用异步配置
:
@Configuration
@EnableAsync
@EnableScheduling
@Profile("!non-async")
public class AsyncConfiguration implements AsyncConfigurer {
// this configuration will be active as long as profile "non-async" is not (!) active
}
@Configuration
// notice the missing @EnableAsync annotation
@EnableScheduling
@Profile("non-async")
public class NonAsyncConfiguration {
// this configuration will be active as long as profile "non-async" is active
}
如果非异步配置文件处于活动状态,则使用非同步配置
:
@Configuration
@EnableAsync
@EnableScheduling
@Profile("!non-async")
public class AsyncConfiguration implements AsyncConfigurer {
// this configuration will be active as long as profile "non-async" is not (!) active
}
@Configuration
// notice the missing @EnableAsync annotation
@EnableScheduling
@Profile("non-async")
public class NonAsyncConfiguration {
// this configuration will be active as long as profile "non-async" is active
}
现在在有问题的JUnit测试类中,我显式激活了“非异步”配置文件,以便相互排除异步行为:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest
@Transactional
@ActiveProfiles(profiles = "non-async")
public class SomeServiceIntTest {
@Inject
private SomeService someService;
@Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
verifyResults();
}
// verifyResult() with assertions, etc.
}
如果您使用的是Mockito(直接或通过Spring测试支持@MockBean
),它有一个验证模式,在这种情况下有一个超时:
你也可以使用等待(在互联网上找到的,还没有尝试过)。
如果您的方法返回CompletableFuture
则使用join
method-
此方法等待异步方法完成并返回结果。遇到的任何异常都将在主线程中重新调用。我已通过注入完成
ThreadPoolTaskExecutor
然后
executor.getThreadPoolExecutor().awaitTermination(1,TimeUnit.SECONDS)强>
在验证结果之前,
报告内容如下:
@Autowired
private ThreadPoolTaskExecutor executor;
@Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
verifyResults();
}
为了扩展@bastiat的答案(我认为这应该是正确的答案),如果您与多个执行者一起工作,您还应该指定TaskExecutor
。因此,您需要注入您希望等待的正确的。那么,让我们假设我们有以下配置类
@Configuration
@EnableAsync
public class AsyncConfiguration {
@Bean("myTaskExecutor")
public TaskExecutor myTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(15);
executor.setCoreCapacity(10);
executor.setQueueCapacity(Integer.MAX_VALUE);
executor.setThreadNamePrefix("MyTaskExecutor-");
executor.initialize();
return executor;
}
// Everything else
}
然后,您将得到一个类似于下面的服务
@Service
public class SomeServiceImplementation {
@Async("myTaskExecutor")
public void asyncMethod() {
// Do something
}
// Everything else
}
@Autowired
private SomeService someService;
@Autowired
private ThreadPoolTaskExecutor myTaskExecutor;
@Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
this.someService.asyncMethod(testData);
this.myTaskExecutor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
this.verifyResults();
// Everything else
}
现在,扩展@bastiat answer,测试将如下所示
@Service
public class SomeServiceImplementation {
@Async("myTaskExecutor")
public void asyncMethod() {
// Do something
}
// Everything else
}
@Autowired
private SomeService someService;
@Autowired
private ThreadPoolTaskExecutor myTaskExecutor;
@Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
this.someService.asyncMethod(testData);
this.myTaskExecutor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
this.verifyResults();
// Everything else
}
此外,我还有一个与问题无关的小建议。我不会将@Transactional
注释添加到服务,只会添加到DAO/存储库。除非您需要将它添加到一个特定的服务方法中,该方法必须是原子的有趣的方法,但这仅在没有spring配置依赖于任务执行器的情况下才有效。如果异步测试是一个问题,我建议将它们转移到一个单独的surefire Execution。您上面提到的好的解决方案是一个可行的解决方案,将asyncMethod结果类型公开为Future,并返回新的AsyncResult(null);在异步方法的最后;在测试方法中,从asyncMethod获取未来并等待它,如future f=someService.asyncMethod(testData);f、 get();验证结果()代码>或者,您可以使用CompletableFuture::Get如果在同一spring上下文中有多个测试,那么您必须注意重新启动执行器,对吗?