web-dev-qa-db-ja.com

Spring @Async voidサービスメソッドのJUnitテスト

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を返すため、ここに適用します。

21
Abdull

ために @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.
}
20
Abdull

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) );
8
jhyot

メソッドがCompletableFutureメソッドを返す場合は、joinメソッドを使用します- documentation CompletableFuture :: join

このメソッドは、asyncメソッドが完了するのを待って、結果を返します。発生した例外は、メインスレッドで再スローされます。

2
Jan Mares

ThreadPoolTask​​Executorを注入することで完了しました

その後

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();
    }
0
bastiat