JUnit5结合Mockito实现Thread.sleep()调用时抛出InterruptedException
如何Mock Thread.sleep()抛出InterruptedException(JUnit5+Mockito)
Thread.sleep()本身的执行逻辑是:如果当前线程持有中断标记,方法会立刻抛出InterruptedException,同时清除中断标记。基于这个特性,你完全不需要做静态Mock,用最轻量化的方式就能覆盖异常分支,这也是优先推荐的方案。
方案1:无需Mock,利用线程原生中断机制(推荐)
直接在调用被测方法前,给当前执行测试的线程设置中断标记即可,Thread.sleep(3000)会自然抛出异常进入catch分支,代码如下:
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class SomeClassTest { private final SomeClass underTest = new SomeClass(); @Test void shouldReturnHelloWhenSleepInterrupted() { // 给当前线程打中断标记 Thread.currentThread().interrupt(); // 调用被测方法,sleep会自动抛出InterruptedException并被内部catch String result = underTest.myMethod(); // 验证方法正常返回结果 assertEquals("Hello", result); } }
这个方案没有任何Mock框架的兼容性问题,执行效率高,完全模拟真实场景下的中断逻辑。因为sleep抛出异常时会自动清除线程中断标记,不会对其他测试用例造成污染。
方案2:使用Mockito静态Mock能力(适合需要精确验证调用逻辑的场景)
如果你需要精确校验Thread.sleep()的调用次数、传入参数,或者你的逻辑中sleep前有其他会清除中断标记的操作,可以用Mockito 3.4+版本内置的静态方法Mock能力实现,不需要引入PowerMock。
注意Thread.sleep是静态native方法,常规的Mockito实例Mock逻辑对其无效。
测试代码如下:
import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import static org.mockito.Mockito.mockStatic; import static org.junit.jupiter.api.Assertions.assertEquals; class SomeClassTest { private final SomeClass underTest = new SomeClass(); @Test void shouldReturnHelloWhenSleepInterruptedByMock() { // 用try-with-resources管理静态Mock生命周期,自动关闭避免跨用例污染 try (MockedStatic<Thread> threadMock = mockStatic(Thread.class)) { // 定义匹配规则:当传入3000调用sleep时抛出异常 // 如果要匹配所有sleep调用,可以把参数换成anyLong() threadMock.when(() -> Thread.sleep(3000L)) .thenThrow(new InterruptedException("mock interrupted")); String result = underTest.myMethod(); assertEquals("Hello", result); // 可选:校验sleep方法确实被调用了1次 threadMock.verify(() -> Thread.sleep(3000L)); } } }
注意事项
- 必须使用Mockito 3.4.0及以上版本,低版本不支持原生静态Mock。
- 静态Mock必须放在try-with-resources块中执行,不要把
MockedStatic实例定义为类级别的共享变量,否则会污染其他测试用例的执行逻辑。 - 参数匹配时注意
Thread.sleep接收的是long类型参数,不要直接传入int值做精确匹配,会导致匹配规则失效。
内容的提问来源于stack exchange,提问作者user19486104
相关产品推荐
相关产品推荐

