Java JUnit 测试 Spring @Async void 服务方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42438862/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:09:45  来源:igfitidea点击:

JUnit-testing a Spring @Async void service method

javaspringjunitspring-async

提问by Abdull

I have a Spring service:

我有一个 Spring 服务:

@Service
@Transactional
public class SomeService {

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

And I have an integration test for this SomeService:

我有一个集成测试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:

这是问题所在:

  • as SomeService.asyncMethod(..)is annotated with @Asyncand
  • as the SpringJUnit4ClassRunneradheres to the @Asyncsemantics
  • SomeService.asyncMethod(..)标注有@Async
  • 因为SpringJUnit4ClassRunner遵守@Async语义

the testAsyncMethodthread 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.

testAsyncMethod线程将呼叫分叉someService.asyncMethod(testData)到自己的工作线程,然后直接继续执行verifyResults(),以前的工作线程完成其工作可能之前。

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<?>.

someService.asyncMethod(testData)在验证结果之前如何等待's 完成?请注意如何使用 Spring 4 和注释编写单元测试来验证异步行为的解决方案不要在这里申请,作为someService.asyncMethod(testData)返回void,而不是一个Future<?>

采纳答案by Abdull

For @Asyncsemantics to be adhered, some active @Configurationclass will have the @EnableAsyncannotation, e.g.

为了@Async遵守语义,一些活动@Configuration类将具有@EnableAsync注释,例如

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

  //

}

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

为了解决我的问题,我引入了一个新的 Spring 配置文件non-async

If the non-asyncprofile is notactive, the AsyncConfigurationis used:

如果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

}

If the non-async profile isactive, the NonAsyncConfigurationis used:

如果非异步轮廓活动的,则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 in order to mutually exclude the async behavior:

现在在有问题的 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.
}

回答by jhyot

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

如果您正在使用 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();

You could also use Awaitility (found it on the internet, haven't tried it). https://blog.jayway.com/2014/04/23/java-8-and-assertj-support-in-awaitility-1-6-0/

你也可以使用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) );

回答by Jan Mares

In case your method returns CompletableFutureuse joinmethod - documentation CompletableFuture::join.

如果您的方法返回CompletableFuture使用join方法 -文档 CompletableFuture::join

This method waits for the async method to finish and returns the result. Any encountered exception is rethrown in the main thread.

此方法等待异步方法完成并返回结果。任何遇到的异常都会在主线程中重新抛出。

回答by bastiat

I have done by injecting ThreadPoolTaskExecutor

我已经通过注入 ThreadPoolTask​​Executor

and then

进而

executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);

executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);

before verifying results, it as below:

在验证结果之前,它如下:

  @Autowired
  private ThreadPoolTaskExecutor executor;

    @Test
    public void testAsyncMethod() {

        Foo testData = prepareTestData();

        someService.asyncMethod(testData);

        executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);

        verifyResults();
    }