JUnit testing the Spring @Async void method

I have a Spring service:

@Service @Transactional public class SomeService { @Async public void asyncMethod(Foo foo) { // processing takes significant time } } 

And I have an integration test for this 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. } 

Here is the problem:

  • how SomeService.asyncMethod(..) annotated using @Async and
  • since SpringJUnit4ClassRunner adheres to @Async semantics

the testAsyncMethod thread will testAsyncMethod call to someService.asyncMethod(testData) into its own workflow, and then continue to verifyResults() , possibly before the previous workflow completes.

How can I wait for someService.asyncMethod(testData) to complete before checking the results? Note that solutions. How do I write unit test to test for asynchronous behavior using Spring 4 and annotations? not applicable here, because someService.asyncMethod(testData) returns void , not a Future<?> .

+6
source share
3 answers

For @Async semantics to stick with, some active @Configuration class will contain @EnableAsync annotation , e.g.

 @Configuration @EnableAsync @EnableScheduling public class AsyncConfiguration implements AsyncConfigurer { // } 

To solve the problem, I introduced a new Spring non-async profile.

If the non-async profile is not active, AsyncConfiguration used:

 @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 } 

If a non-asynchronous profile is active Used by 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 } 

Now in the problematic JUnit test class, I explicitly activate the "non-async" profile to mutually exclude async behavior:

 @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. } 
+8
source

If you use Mockito (directly or through Spring @MockBean testing @MockBean ), it has a verification mode with a timeout for this particular case: https://static.javadoc.io/org.mockito/mockito-core/2.10.0/org /mockito/Mockito.html#22

 someAsyncCall(); verify(mock, timeout(100)).someMethod(); 

You can also use Awaitility (find it on the Internet, have not tried). 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) ); 
+2
source

In case your method returns the CompletableFuture join method, the documentation is CompletableFuture :: join .

This method waits for the completion of the asynchronous method and returns the result. Any detected exception is returned to the main thread.

0
source

Source: https://habr.com/ru/post/1015248/


All Articles