javaspringjunitspring-async

JUnit-testing a Spring @Async void service 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:

the testAsyncMethod thread will fork the call someService.asyncMethod(testData) into its own worker thread, then directly continue executing verifyResults(), possibly before the previous worker thread has finished its work.

How can I wait for someService.asyncMethod(testData)'s completion before verifying the results? Notice that the solutions to How do I write a unit test to verify async behavior using Spring 4 and annotations? don't apply here, as someService.asyncMethod(testData) returns void, not a Future<?>.


Solution

  • For @Async semantics to be adhered, some active @Configuration class will have the @EnableAsync annotation, e.g.

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

    To resolve my issue, I introduced a new Spring profile non-async.

    If the non-async profile is not active, the AsyncConfiguration is 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 the non-async profile is active, the NonAsyncConfiguration is used:

    @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 in order to mutually exclude the 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.
    }