Java 我如何在单元测试中验证方法是否会异步执行,即在单独的线程中执行?

Java 我如何在单元测试中验证方法是否会异步执行,即在单独的线程中执行?,java,mockito,awaitility,Java,Mockito,Awaitility,以下方法同步调用服务的方法serveThis(),并在单独的线程(即异步)中调用方法serveThis(): 我想在单元测试中验证service.serveThat()将异步执行,因为根据规范,它不能同步执行。 所以,我想防止以后有人删除启动一个新线程,如下所示: public void doSomething() { service.serveThis(); // This synchronous execution must cause the test to fail

以下方法同步调用
服务的方法
serveThis()
,并在单独的线程(即异步)中调用方法
serveThis()

我想在单元测试中验证
service.serveThat()
将异步执行,因为根据规范,它不能同步执行。 所以,我想防止以后有人删除启动一个新线程,如下所示:

public void doSomething() {
    service.serveThis();
    // This synchronous execution must cause the test to fail
    service.serveThat();
}

为了实现这一点,我使用Mockito:

Thread threadServeThatRunsOn;

@Test
public void serveThatWillBeExecutedAsynchronously() throws Exception {
    doAnswer(invocation -> {
        threadServeThatRunsOn = Thread.currentThread();
        return null;
    }).when(mockService).serveThat();

    testObject.doSomething();

    verify(mockService, timeout(200)).serveThat();
    assertNotEquals(threadServeThatRunsOn, Thread.currentThread());
}
现在,如果有人修改
doSomething()
,使
service.serveThat()
同步运行,则此测试将失败

我想验证
service.serveThat()
是否将异步执行


会的。语法是这样说的。不要测试平台。

可以在代码中的任何地方获取
Thread.currentThread()
,因此您可以编写类似的内容,并根据它进行断言(如果您没有
接口,您可以使用Mockito以不同的方式实现此目标)

单元测试可以是这样的,但是可能需要基于用例的附加断言:

ThreadChecker mockService = new ThreadChecker(); 

@Test
public void serveThatWillBeExecutedAsynchronously() throws Exception {
    doSomething();
    TestCase.assertFalse(mockService.serveThatThread == mockService.serveThisThread);
}

在单元测试中使用thread.sleep是一种非常糟糕的做法。单元测试应该是超快速的,并且在毫秒的任意等待下,测试本质上是脆弱的。@pcjuzer可以随意改进这种方法,因为Java中多线程的性质,异步执行某些内容本身并不能保证稍后会完成。当异步启动的线程提前完成时,可能会出现异常情况。多线程行为不是可以很好地(单元)测试的东西。在您的情况下,我可能会选择一个实现,其中serveThat()不能直接调用,只需通过一个总是启动新线程的包装器即可。@pcjuzer该测试确实保证
serveThat()
将在以后完成,因为它使用Mockito的
doAnswer()来插入调用
为了实现这一点,500毫秒的睡眠确实可以很好地保证顺序,但是,在500毫秒内,可以运行200个正常的单元测试,而不是这一个。编写持续半秒的单元测试是不可伸缩的。也可以将500毫秒调整为一个更低的值,但在这种情况下,你失去了对计时的保证,这就变成了一个数字游戏。谢谢,我已经改进了Naming。没有任何必要的改变。当通过
新线程(()->service.serveThat()).start()执行时,您仍在询问如何检查
service.serveThat()
是否在单独的线程中运行。是的。Javadoc和Java语言规范保证了这一点。不要测试平台。@AndrasHatvani这个问题是关于平台的单元测试,它不是单元测试的正确用途。如果新线程(…)出现问题。start()。您不需要在应用程序单元测试中测试平台。否则,我们将不得不测试
if
else
while
。。。我们没有。我们不能。因为在单元测试中,我们将完全依赖于我们试图测试的行为。你必须在某个地方停下来。在你众多的编辑中,没有任何一个能够解决根本问题。Java已经保证了这种行为,并且在发布之前,它已经接受了数百或数千个自己的单元测试。您需要担心应用程序是否满足其契约:而不是Java是否满足。您只是在浪费时间。@EJP,OP的
doSomething
函数是要测试的方法,而不是测试方法。它会受到更改的影响,因此通过测试来保护其行为似乎很自然,以确保更改不会破坏其异步行为。我的问答不是关于测试平台,而是关于防止同步执行serveThat()的问题。此外,您的代码片段甚至不是一个测试。此建议为测试目的的生产代码增加了不必要的复杂性,并公开了技术细节。此外,它不会等待线程的初始化,
serveThat()
将在其上运行。我不建议在这里修改任何生产代码。你完全误解了。我认为这实际上是一个很好的答案。生产代码没有变化,因为据说Mockito可以完成模拟接口。检查线程对象比基于任意睡眠时间的当前解决方案更好/更安全。PS:使用mockito,您可以首先定义一个
AtomicReference
,然后:`mockito.doAnswer(调用->{threadRef.set(Thread.currentThread());返回null;}.when(mockService.serveThat();`在等待执行之后,检查
assertNotEquals(Thread.currentThread(),threadRef.get())
而不是处理原始线程,为什么不使用抽象
Future
?然后你可以让你的方法契约返回一个
Future
,并针对它编写测试。@在这种情况下,这真的只是一个fire'n'forget动作,处理一个Future会与这个事实相矛盾。
public class ThreadChecker implements YourInterface {

    volatile Thread serveThisThread;
    volatile Thread serveThatThread;

    public void serveThis() {
        serveThisThread = Thread.currentThread();
    }

    public void serveThat() {
        serveThatThread = Thread.currentThread();
    }
}
ThreadChecker mockService = new ThreadChecker(); 

@Test
public void serveThatWillBeExecutedAsynchronously() throws Exception {
    doSomething();
    TestCase.assertFalse(mockService.serveThatThread == mockService.serveThisThread);
}