2017-02-24 177 views
6

我有一個春季服務:的JUnit測試春天@Async無效服務方法

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

這裏的問題是:

  • as SomeService.asyncMethod(..)@Async
  • 註釋
  • SpringJUnit4ClassRunner粘附在@Async語義

testAsyncMethod線程將呼叫分叉someService.asyncMethod(testData)到自己的工作線程,然後直接繼續執行verifyResults(),以前的工作線程完成其工作可能之前。

在驗證結果之前,我如何等待someService.asyncMethod(testData)完成?請注意,How do I write a unit test to verify async behavior using Spring 4 and annotations?的解決方案在此不適用,因爲someService.asyncMethod(testData)返回void,而不是Future<?>

回答

8

對於@Async要遵守的語義,some active @Configuration class will have the @EnableAsync annotation,例如,

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

    // 

} 

爲了解決我的問題,我引入了一個新的Spring配置文件non-async

如果non-async輪廓活躍,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. 
} 
1

如果您正在使用的Mockito(直接或通過彈簧試驗支持@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)); 
0

如果你的方法返回CompletableFuture使用join方法 - documentation CompletableFuture::join

此方法等待異步方法完成並返回結果。任何遇到的異常都會在主線程中重新生成。