Java 如何用Mockito模拟Thread.class?

Java 如何用Mockito模拟Thread.class?,java,mockito,Java,Mockito,作为单元测试的一部分,我尝试使用Mockito模拟Thread.class isAlive()方法以返回true。下面是我的代码: final Thread mockThread = Mockito.mock(Thread.class); Mockito.when(mockThread.isAlive()).thenReturn(true); 这给了我第二行的以下例外情况: 线程“main”org.mockito.exceptions.misusing.MissingMethodInvoca

作为单元测试的一部分,我尝试使用Mockito模拟Thread.class isAlive()方法以返回true。下面是我的代码:

final Thread mockThread = Mockito.mock(Thread.class);
Mockito.when(mockThread.isAlive()).thenReturn(true);
这给了我第二行的以下例外情况:


线程“main”org.mockito.exceptions.misusing.MissingMethodInvocationException中的异常:
when()需要的参数必须是“模拟的方法调用”。
例如:
when(mock.getArticles())。然后返回(articles);
此外,出现此错误的原因可能是:
1.您可以存根:final/private/equals()/hashCode()方法。
这些方法无法存根/验证。
不支持对非公共父类声明的模拟方法。
2.在when()中,您不在mock上调用方法,而是在其他对象上调用方法。

我已经多次以这种方式使用Mockito,没有任何问题。mocking Thread.class有什么问题吗?我四处搜索,但运气不佳。

Thread.isAlive()
是用
final
修饰符声明的。它不能被Mockito 1模仿。
使用Powermock或Mockito 2添加:

很长一段时间,当Mockito拒绝时,我们的用户感到难以置信 模拟最后一节课。 ... 模拟最终的类和方法是一个酝酿中的、选择加入的特性

或者另一种方式:改变单元测试类的方式。

您真的需要依靠
isAlive()
进行单元测试吗?

如果您真的需要它,您还可以使用一个包装器类来组成
线程
实例,并将处理委托给组成的
线程

例如,这可以实现
Runnable


通过这种方式,您可以自然地模拟这个包装类的
isAlive()
方法。

看起来像是Thread.isAlive方法,并且不能用Mockito模拟最终的方法


这里有一个。

正如其他人所指出的,线程的isAlive方法是最终的,因此不能(也可能不应该)使用Mockito进行模拟

一个简单的解决方法是使用run()方法在我的单元测试类中创建一个私有可运行类,该方法只调用this.wait()

然后,我可以在单元测试中使用这个实例创建一个线程,并且isAlive()方法将始终返回true

final TestRunnable testRunnable = new TestRunnable();
final Thread expectedThread = new Thread(testRunnable);
expectedThread.start();
然后在后面的测试中

assertTrue(expectedThread.isAlive());
最后,要清理线程,只需在可运行实例上调用notify()(当然,当JUnit也完成时,线程将结束)


我给你一个完全不同的视角:完全摆脱与生产代码中线程的交互

事实是:线程是多线程的一个非常低级的抽象。10、15年前,我们只有线程,因此被迫使用它们

但是现在,你有大量的抽象概念在更高的层次上工作,比如,和,承诺,和

这听起来可能很奇怪,但是对于你来说,退一步考虑不要使用你今天使用的线程可能会更有帮助。你看,有些东西允许你将多线程代码转换成单线程进行测试——只需提供一个不同的服务来执行任务


这是您在2017年应该努力争取的抽象级别-您不应该浪费时间在模仿低级最终方法上

isAlive
是一个final方法,因为错误说明您不能存根这些方法。hanks,参考文章对PowerMock的解决方案有非常有用的解释。更正:Mockito的最新版本确实支持模拟final类/方法。这是实验性的,但我仍然更喜欢实验性的Mockito而不是普通的PowerMock(ito)。谢谢,我没有意识到isAlive()是final,并且不能用Mockito模拟final方法(应该更仔细地阅读错误)。谢谢你的建议。对于我的测试用例,我添加了一个私有的可运行类,它只在同步块中调用wait()。这样,我可以调用并使isAlive()为true,并通过调用notify()终止线程。再次感谢您的快速回答。不客气:)我编辑了答案以参考Mockito 2。你解决这个问题的方法似乎很有趣。不要犹豫,随信附上答案。它可以帮助其他人。@GhostCat更好:)新手的好答案!就个人而言,我只需要做
新线程(()->{while(true){})@Michael一开始我和你一样认为。但是考虑到这一点,单元测试必须尽可能快地执行。在某些情况下,我们甚至可以将它们并行化
wait()
效率更高,因为它不消耗CPU内核/线程,所以我认为它应该优于繁忙的等待。@davidxxx从不过早地优化。:)@迈克尔:对。但是对于单元测试执行时间,我违反了规则;-)@Michael Ah Lambda,我们仍然需要成为朋友:)同意davidxxx的评论,但这就是我选择事件驱动范式的原因。@davidxxx我同意。但是很难给出一个具体的例子——因为OP只询问Thread.alive()——他没有让我们深入了解他的生产代码——而且已经有了一个很好的、公认的答案。那么,发明人工例子就没什么意义了。你没有错。也许,这会给下一个问题提供思路,你会有机会用一个具体的案例来说明:)@GhostCat感谢你的洞察力。起初,出于您提到的原因,我考虑避免直接使用Thread.class,但经过进一步思考,我决定使用它。我的用例是,我正在与一个API交互,该API限制每分钟请求数和每天请求数,并向您收取超出这些限制的“超额”费用,因此客户端必须跟踪自己的使用情况。问题是,直到第一个请求发出,“天”或“分钟”才开始。这排除了任何调度程序服务。我发现直接使用线程和同步/阻塞来限制/跟踪剩余的al更容易
assertTrue(expectedThread.isAlive());
synchronized(testRunnable) {
    testRunnable.notifyAll(); // kill the Runnable (prob not necessary)
}