Springサービスがあります:
_@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.
}
_
問題は次のとおりです。
SomeService.asyncMethod(..)
には_@Async
_注釈が付けられ、SpringJUnit4ClassRunner
_は_@Async
_セマンティクスに準拠しているためtestAsyncMethod
スレッドは、呼び出しsomeService.asyncMethod(testData)
を独自のワーカースレッドに分岐し、おそらく以前のワーカースレッドが作業を完了する前に、verifyResults()
の実行を直接続行します。
結果を検証する前にsomeService.asyncMethod(testData)
の完了を待つにはどうすればよいですか? Spring 4と注釈を使用して非同期動作を検証する単体テストを作成するにはどうすればよいですか?の解決策に注意してくださいsomeService.asyncMethod(testData)
は_Future<?>
_ではなくvoid
を返すため、ここに適用します。
ために @Async
準拠するセマンティクス、 一部のアクティブ@Configuration
クラスには@EnableAsync
アノテーション 、例:.
@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
//
}
問題を解決するために、新しいSpringプロファイルnon-async
。
non-async
プロファイルはnotアクティブであり、AsyncConfiguration
が使用されます:
@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
}
非非同期プロファイルがアクティブな場合、NonAsyncConfiguration
が使用されます。
@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
)、この場合にはタイムアウトの検証モードがあります: https://static.javadoc.io/org.mockito/mockito-core/2.10.0/org/mockito/Mockito.html #22
someAsyncCall();
verify(mock, timeout(100)).someMethod();
また、Awaitilityを使用することもできます(インターネットで見つけましたが、試していません)。 https://blog.jayway.com/2014/04/23/Java-8-and-assertj-support-in-awaitility-1-6-0/
someAsyncCall();
await().until( () -> assertThat(userRepo.size()).isEqualTo(1) );
メソッドがCompletableFuture
メソッドを返す場合は、join
メソッドを使用します- documentation CompletableFuture :: join 。
このメソッドは、asyncメソッドが完了するのを待って、結果を返します。発生した例外は、メインスレッドで再スローされます。
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();
}